leetcode小白刷题之旅----3. Longest Substring Without Repeating Characters
仅供自己学习,有借鉴成分。
题目:
Given a string s, find the length of the longest substring without repeating characters.
思路:
1.下意识想到暴力是可以求解的,把所有无重复字母子串记录下来再来比较。但从每个字母开始遍历则总RT为O(n^2)
2.除了暴力后,自己在想的方法都很麻烦,而且和暴力求解差别不大。看了题解了解到一个滑动窗口的算法。简单来说就是用两个tag,一个记录子串最左元素位置,另一个记录子串最右元素位置。主要判断即查看子串中是否有相同元素,无则右tag拓展,否则将左tag移动到该子串中相同的元素后。了解到这个算法后思路就比较清晰了,下面是一些细节和技巧的处理。
(1).使用哈希表以S里的每个元素为key值,其下标为value,可以以O(1)的速度判断是否有重复的元素。
(2).更为简便的方法。将循环递增的 i 视为右tag,若要获得子串长度只需要 i-left ,这也就将left设在子串最左元素的前一个元素位置。
(3).此种方法会导致一个特殊情况,就是left tag因为重复元素移动后可能导致现在的子串长度比上一个子串长度更短,故子串长度 maxlen应该取 (maxlen,i-left)的最大值
(4).哈希表能迅速判断该元素是否被映射过,但如何判断在子串当中呢。即在该判断条件下增加 m[s[i]] > left即可,因为value是该元素在字符串的位置,故可判断是否在子串内。
综上,算法只遍历一遍字符串,故RT为O(n)
代码
这里第6行的for循环,并不需要先遍历所有字符串元素一一映射后在进行操作。这样先判断后映射并不会影响最终结果,并且当遇到第一个重复元素前,maxlen都在增,left不变,每个映射都被记录。直到left移动后,之前映射的关系都足够之后的判断,否则不会进入if,继续映射下去
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 unordered_map <char,int> m; 5 int maxlen=0,left=-1; 6 for(int i=0;i<s.size();i++) 7 { 8 if(m.count(s[i]) && m[s[i]] > left ) 9 left = m[s[i]]; 10 m[s[i]] = i; 11 maxlen = max(maxlen,i-left); 12 } 13 return maxlen; 14 } 15 };
再看他人题解后了解到还有unordered_set 容器。其结构是一种hash表,而unordered_map 是以hash表为基础结构的字典。
思路:
如果遍历到的S里的元素未被加入进hash表里则将他加入,并且判断是否需要更新最大子串长度。如果该元素已经添加过了,即存在重复元素,则 i 此时不会改变,hash表调用erase函数将与传入参数相同的元素删除,因为是left++,会一直将重复元素之前(包括重复元素)的所有元素删除,此时left也在新子串的最左边的元素位置,下一次循环 i 就开始增加,即右tag的扩容。
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 unordered_set <char> m; 5 int left=0,l=s.size(),maxlen=0,i=0; 6 while(i<l) 7 { 8 if(!m.count(s[i])) 9 { 10 m.insert(s[i++]); 11 maxlen = max(maxlen,(int)m.size()); 12 } 13 else 14 m.erase(s[left++]); 15 } 16 return maxlen; 17 } 18 };