LeetCode3 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. (Medium)
解法1
自己开始AC的想法,用两重循环,搜索以s[i]开头的字串中的最长无重字串,所以一旦有重复元素出现(利用letters判重),内层的循环就可以直接跳出;
复杂度不会超过O(256(n)),因为内层最对走256步必有重复,可以不再走下去。 时间跑出来是60ms
代码1:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int result = 0; 5 int letters[256] = {0}; 6 int tempResult; 7 for (int i = 0; i < s.size(); ++i) { 8 if (i + result >= s.size()) { 9 break; 10 } 11 tempResult = 1; 12 memset(letters, 0 , sizeof(letters)); 13 letters[s[i]] = 1; 14 for (int j = i + 1; j < s.size(); ++j) { 15 if (letters[s[j]] == 0) { 16 tempResult++; 17 letters[s[j]] = 1; 18 } 19 else { 20 break; 21 } 22 } 23 result = max(result, tempResult); 24 } 25 return result; 26 } 27 };
解法2
滑动窗口方法
用left记录当前考察的可行字串的最左端,i循环遍历整个字串。
如果遇到不重复元素,则添加进hash表内,并更新最大值;
如果遇到重复元素,将left开始递增,直至跳过重复元素,然后同上操作,将该元素添加进hash表,并更新最大值。
代码2利用unodered_set实现(估计find方法效率又低了,只有72ms), 代码3利用单独开的数组实现,效率更高(16ms)。
盗用网上一幅图,出处不详...
复杂度O(2n) = O(n) ; left,i各自遍历一遍O(2n)
代码2:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 unordered_set<char> hash; 5 int left = 0; 6 int result = 0; 7 for (int i = 0; i < s.size(); ++i) { 8 if (hash.find(s[i]) != hash.end()) { 9 while (s[left] != s[i]) { 10 hash.erase(s[left]); 11 left ++; 12 } 13 hash.erase(s[left]); 14 left ++; 15 } 16 hash.insert(s[i]); 17 result = max(result, i - left + 1); 18 } 19 return result; 20 } 21 };
代码3:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int letters[256] = {0}; 5 int left = 0; 6 int result = 0; 7 for (int i = 0; i < s.size(); ++i) { 8 if (letters[s[i]] != 0) { 9 while (s[left] != s[i]) { 10 letters[s[left]] = 0; 11 left ++; 12 } 13 letters[s[left]] = 0; 14 left ++; 15 } 16 letters[s[i]] = 1; 17 result = max(result, i - left + 1); 18 } 19 return result; 20 } 21 };
解法3
解法3是在2的基础上的一个简单优化, letters数组只用来保存存在与否有些浪费,同时用while循环递增left找到重复元素出现位置下一位效率略低;
可以将letters数组改为存储某个字符最近出现的位置lastIndex[256],这样更新left时,直接将left = lestIndex[s[i]] + 1即可, 将 O(2n)变为真正的一次循环 O(n)
代码4:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int lastIndex[256]; // 上次出现该字符的下标 5 int left = 0; //当前维护的字串的最左端 6 int result = 0; 7 memset(lastIndex, -1, sizeof(lastIndex)); 8 for (int i = 0; i < s.size(); ++i) { 9 if (lastIndex[s[i]] >= left) { //在这个字串内有重复 >= left 10 left = lastIndex[s[i]] + 1; 11 } 12 lastIndex[s[i]] = i; 13 result = max(result, i - left + 1); 14 } 15 return result; 16 } 17 };