LeetCode——3. Longest Substring Without Repeating Characters


一.題目鏈接:https://leetcode.com/problems/longest-substring-without-repeating-characters

二.題目大意:

  給定一個字符串,返回它最長的無重復的子串的長度。

三.題解

  該題目,我用了三種方法:

1.思路一:暴力解決,直接用兩層for循環對目標字符串所有的子串進行判斷。代碼如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.length();
        string str;//表示最長子串
        int max_len = 0;
        for(int i = 0; i < len; i++)
        {
            string t_str;//中間變量;
            for(int j = i ; j < len; j++)
            {
                t_str = t_str + s[j];
                if(isUnique(t_str) == 1)
                {
                    if(t_str.length() > max_len)
                     max_len = t_str.length();
                }
                else
                    break;
            }
        }
        return max_len;

    }
    //判斷字符串是否包含重復元素(利用了哈希表,所以時間復雜度為O(n))
    int isUnique(string s)
    {
       int len = s.length();
       unordered_map<char,int> _map;
       unordered_map<char,int>::iterator it;
       for(int i = 0 ; i < len; i++)
       {
           it = _map.find(s[i]);
           if(it == _map.end())
            _map.insert(pair<char,int>(s[i],1));
           else
            return 0;
       }
       return 1;
    }
};

  isUnique()用於判斷字符串是否包含重復的元素,由於用的是unordered_map(由哈希表實現,時間復雜度為O(1)),該函數的時間復雜度的為O(n);lengthOfLongestSubstring(string s)方法的時間復雜度為O(n2),故總的時間復雜度為O(n3),空間復雜度為O(n)。

提交結果:超時!

2.對方法一進行改,發現子串判斷是否包含重復元素的過程可以在遍歷過程中實現,所以就沒必要單獨定義一個方法了。代碼如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.length();
        string str;//表示最長子串
        int max_len = 0;
        for(int i = 0; i < len; i++)
        {
            string t_str;//中間變量;
            unordered_map<char,int> _map;
            unordered_map<char,int>::iterator it;
            for(int j = i ; j < len; j++)
            {
                it = _map.find(s[j]);
                if(it == _map.end())
                {
                     t_str = t_str + s[j];
                    _map.insert(pair<char,int>(s[j],1));
                    if(t_str.length() > max_len)
                     max_len = t_str.length();
                }
                else
                    break;
            }
        }
        return max_len;

    }
};

  這種方法,把unordered_map查詢和插入的過程融合到for循環中,總的時間復雜度為O(n2),相比方法一時間上縮小了一個數量級。

提交結果:Accepted,耗時786ms.

3.看提交結果只打敗了0.02%cpp提交。還可不可以再進行改進呢?

思路三:對於一個字符串,例如qpxrjxkltzyx,我們從頭開始遍歷時,如果遇到一個之前出現過的字符的話,例如:qpxrjx,那么從第一個x到第二個x的這段子串,一定不包含在我們的最終結果之中,我們需要記錄的子串為:qpxrj;而且下一次的起始位置是第一個x之后的那個位置;這樣指導最后一個字符;這實質可以看成一種貪心(子問題的性質決定了父問題的性質)。如圖所示:

代碼如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        vector<int>dict(256,-1);//此處的vector相當於一個哈希表
        int max_len = 0;
        int len = s.size();//string的size()方法和length()方法內部實現其實完全一樣,只不過容器都有size()方法,此處是為了滿足不同人的習慣
        int start = -1;//起始位置
        for(int i = 0; i < len; i++)
        {   //遇到重復出現的字符,則start的位置變成重復的那個字符之前出現的位置
            if(dict[s[i]] > start)
               start = dict[s[i]];//本來start應該為dict[s[i]]+1的,但后面計算長度時,還需在加個1,相當於i-start+1,所以1相當於抵消了
           max_len = max(max_len, i - start);//每次迭代長度都更新
           dict[s[i]] = i;
        }
        return max_len;

    }
};

  需要注意的點都在注釋中說明了,尤其需要注意的是start的賦值此處為了簡便,把1去掉了;每次迭代長度更新相當於記錄了當前子串的最大長度。此外start的初始值為-1,因為位置0處的字符也要考慮。i一直是增加的,只是start的位置在變化。

該算法的時間復雜度為O(n),空間復雜度為O(1),相比方法2,時間又縮短了一個數量級。

提交結果:Accepted,耗時18ms.

4.此外,如果本題要求的結果是最長無重復子串的話怎么辦?可以設置兩個標記s和e,用於記錄每次更新max_len的i和start,最終的子串即以位置s開以位置e結尾的子串。

5.《劍指offer(第二版)》面試題48,給出了利用動態規划的思想求解,具體思路參考《劍指offer》,具體代碼如下:

class Solution {
public:
    int lengthOfLongestSubstring(string str) {
      int max_len = 0;//最大值
    int cur_len = 0;//以當前位置的字符結尾的不含重復字符的字符串的最大長度,對應f(i)
    int pos[255] = {0};//記錄字符上一次出現的位置
    memset(pos,-1,sizeof(pos));
    int len = str.size();
    for(int i = 0; i < len; i++)
    {
        int pre_index = pos[str[i]];
        if(pre_index < 0 || i - pre_index > cur_len)
            cur_len++;
        else
        {
            cur_len = i - pre_index;
        }
        pos[str[i]] = i;
        if(cur_len > max_len)
            max_len = cur_len;
    }
    return max_len;
    }
};

此方法的時間復雜度同思路三一樣,都是O(n),需要注意的是:

如果令dp[i]表示以第i個字符為結尾的不包含重復字符的子串的最長長度,那實際上cur_len這個變量就表示的是dp[i],即這段代碼中沒有顯示的利用dp[i]的形式,因為沒必要(我們求得是最大值,當前的結果只受前一個結果的影響,所以一個變量就能解決,沒必要把所有的結果都存起來)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2024 CODEPRJ.COM