1. 程式人生 > >LeetCode 3. Longest Substring Without Repeating Characters 給定一個字串,找到它的沒有重複字元的最大子串

LeetCode 3. Longest Substring Without Repeating Characters 給定一個字串,找到它的沒有重複字元的最大子串

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given "abcabcbb", the answer is "abc", which the length is 3.

Given "bbbbb", the answer is "b", with the length of 1.

Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring

"pwke"is a subsequence and not a substring.

Instead of using a set to tell if a character exists or not, we could define a mapping of the characters to its index. Then we can skip the characters immediately when we found a repeated character.

The reason is that if s[j]s[j] have a duplicate in the range [i, j)[i,j) with index j'j​′​​, we don't need to increase ii little by little. We can skip all the elements in the range [i, j'][i,j​′​​] and let ii to be j' + 1j​′​​+1 directly.

If we know that the charset is rather small, we can replace the Map with an integer array as direct access table.

Commonly used tables are:

  • int[26] for Letters 'a' - 'z' or 'A' - 'Z'
  • int[128] for ASCII
  • int[256] for Extended ASCII
    int lengthOfLongestSubstring(string s) {
        int start = 0,length = 0,len=s.size();
        std::vector<int> flag(256,-1);//帶位掩碼記錄每個字母的最後位置
        for(int i=0;i!=len;++i){
            if(flag[s[i]]>=start){ //i處的字元第二次出現時就更新最大長度和start的位置
                length = std::max(length,i-start);
                start = flag[s[i]]+1;
            }
            flag[s[i]] = i;
        }
        return std::max(length,len-start);
        //如果直接返回length,則遇到輸入s為空字元或者沒有重複字元的字串時會返回0
    }
int lengthOfLongestSubstring(string s) {
	int n = s.length();
	int i = 0, j = 0;
	bool exist[256] = { false };
	int max_length = 0, cur_length = 0;
	while (j < n) {
		if (!exist[s[j]]) {
			exist[s[j]] = true;
			++j;
		}
		else {
			max_length = max(max_length, j - i);
			while (s[i] != s[j]) {
				exist[s[i]] = false;
				++i;
			}
			++i;
			++j;
		}
	}

	return max(max_length, n - i);
}

這個解答的時間複雜度是O(N)。雖然有兩個while的巢狀,但是時間複雜度依然是O(N),為什麼呢?

因為i和j都只把這個string從開始到結束遍歷了一遍。

可以這樣想,外層while在改變j的值,j最多從0改變到n(n為字串的長度),內層while在改變i的值,同樣的,i最多從0改變到n(n為字串的長度)。所以加起來,時間複雜度為O(2*N),也就是O(N)。

還可以這樣想,內層迴圈不一定要進行的,僅僅當j遇到了重複字元後需要更新i的值時,才會進行記憶體迴圈,而且i加起來走過的步數最多為n(n為字串的長度)。

這段程式碼還有很有意思的一點,就是別忘了在迴圈體之外,返回值為max(max_length, n-i)。這是為什麼呢? 因為可能最後一次檢查的時候,j知道走到字串末尾都沒有遇到重複字元。而while迴圈體中找到的最長不重複子串只是在j遇到重複字元時才進行的。