题目:给定一个字符串,找出不含有重复字符的最长子串的长度
由于在做前两道题的时候刚刚学习了hashmap的用法,因此在看到这道题目的时候首先想到的就是使用hashmap来完成,哈希表中元素的映射关系可以省去穷举过程中每一次循环都要将字符串全部遍历一遍所消耗的时间,那么时间复杂度基本上不会很高了,接下来要考虑的就是如何实现。
对于代码的实现一开始我并没有一个清晰的思路,只是觉得每次都需要对两个元素进行比较,但是并不清楚该如何去做,后来学习了一下官方题解以及讨论区里各位大神的代码意识到有滑动窗口这么个东西(以前学习过滑动窗口式编码,但是没有能够第一时间学以致用啊,惭愧),那么问题的关键就是如何去定义这个窗口的边界尤其是左边界(在编译过程中博主在这里卡了很久),最终的方案也是参考一位大神的算法给出的,总的来说就是从从字符串头部一个接一个字符进行遍历,每到一个新字符就在前方查看该字符是否曾经出现过,若出现过则把前方出现过的字符剔除出窗口,将后一个字符添加进来,同时更新左边界,下边贴出几种解法的代码,这几种代码的逻辑基本都是相同的:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int len = s.length(); 5 unordered_map <int, int> tmp; 6 int sum = 0, start = 0; 7 for(int i = 0; i < len; i++) 8 { 9 if(tmp.count(s[i]) == 0 || tmp[s[i]] < start) 10 sum = max(sum, i - start + 1); 11 else 12 start = tmp[s[i]]; 13 tmp[s[i]] = i + 1; 14 } 15 return sum; 16 } 17 };
注意上述代码中tmp[s[i]] < start的作用,因为有的字符可能以前出现过而且在滑动窗口左侧,如果不对这种情况加以判断,那么很可能就会直接把这个元素略过,从而导致结果不正确,所以需要对这种情况进行判断。
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int len = s.length(); 5 int start = -1, sum = 0; 6 vector<int> cal(256, -1); 7 //int cal[256] = {[0 ... 255] = -1}; 8 for(int i = 0; i < len; ++i) 9 { 10 start = max(start, cal[s[i]]); 11 cal[s[i]] = i; 12 sum = max(sum, i - start); 13 } 14 return sum; 15 } 16 };
上述代码数组初始化的时候出了问题,博主一开始用的是cal[256] = {-1},但是结果一直出错,后来得知这种初始化方式只给cal[0]完成了赋值,其余的都是系统默认值,注释掉的一句是其他网友给出的答案,但是结果并不正确,最终确认了vector类可以实现不用循环完成大数组初始化。
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int len = s.length(); 5 int j = 0, sum = 0; 6 int n[256] = {0}; 7 for(int i = 0; i < len; i++) 8 { 9 n[s[i]]++; 10 while(n[s[i]] > 1) 11 { 12 n[s[j]]--; 13 j++; 14 } 15 sum = max(sum, i - j + 1); 16 } 17 return sum; 18 } 19 };
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int l = 0 , h = 0; 5 int result = 0; 6 int cal[256] = {0}; 7 while( h < s.size() ){ 8 int len = 0; 9 cal[s[h]] ++; 10 while( cal[s[h]] > 1 ){ // if cal[s[h]] > 1 , we can judge that s[h] appears more than one between s[l] to s[h] 11 cal[s[l]]--; // increase l by one , cal[s[l]] will be reduced until s[h] == 1 . 12 ++ l; // it means that s[h] appears once between s[l] to s[h] 13 } 14 result = max( result , h - l + 1 ); 15 ++ h; 16 } 17 return result; 18 } 19 };
上述两个代码都是讨论区其他大神的解法,我觉得也比较巧妙,但是第一个里边数组初始化的方式也是不对的,只不过他是要初始化为0刚好与系统默认值相同才编译通过的。