标签:简单 去重 改变 判断 访问 char turn span 不重复
第三道题,和前面的那个第一道题数组里面的两个元素求和有点像,题目如下:
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.
大致意思就是,输入一个string,找到在这个string中最长的一个substring满足在这个substring中没有重复的字符,返回这个substring的长度
比如给出abcabcbb这个字符串,abc就是最长的没有重复字符的字符串,最终返回的结果就是3.
一开始,想到的办法是使用一个循环遍历字符串,使用hashmap记录没有重复的连续的字符和它们对应的在字符串中的下标位置,每一次循环都判断当前访问的元素是否已经存在在hashmap中,如果不存在,则把这个新的元素和它的下标加到hashmap中,如果存在,表示重复了,这时候循环就需要从这个重复元素在hashmap中的下标的下一个元素重新开始,同时需要把hashmap清空。
在用上面的思路写完代码以后,在测试集测试的过程中在面对像abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz这样的很长的而且不断按照同一个字符子串循环的就会出现Time limit的错误,就按照上面的字符串来说,当遍历到第二个子字符串abc..的a的时候会和前面已经放在hashmap中的位于第一个子字符串的a重复,这时候,进行的操作就是清空hashmap的同时把遍历指针放到第一个子字符串的b的位置重新开始,而上面的情况在重新遍历到第二个子字符串的b的位置的时候又会发生,这样不断循环重复遍历,实际上最大的不重复子串的长度是没有发生改变的,也就是后面进行的操作都是无用工,如果假设总字符串长度是m,存在的循环子字符串的长度是n,这时候的开销大概是m*n。
上面的这种思路使用javascript来描述:
1 var lengthOfLongestSubstring = function(s) { 2 var pattern={}; 3 var i=0; 4 var longest_length=0; 5 var length=0; 6 while(i<s.length){ 7 if(s[i] in pattern){ 8 var dul_index=pattern[s[i]]; 9 i=dul_index+1; 10 pattern={}; 11 length=0; 12 } 13 else{ 14 pattern[s[i]]=i; 15 i++; 16 length++; 17 if(length>longest_length){ 18 longest_length=length; 19 } 20 } 21 } 22 } 23 return longest_length; 24 };
解决这个问题,改进上面的思路,加了一个判断位,如果当前的元素在hashmap中,不着急去重新遍历,而是继续访问下一个元素,如果下一个元素也存在在hashmap中,表示这时候重新访问只会增加开销,早晚都会碰到重复,而且得到的长度肯定比之前的短,这样的元素应该跳过,而不是在不断的重复访问,同时要记录这些连续在hashmap中存在的元素对应的下标中最大的那个,这样一旦碰到一个不在hashmap中的元素,遍历指针就只需要跳回到最大的重复元素下标的位置就行了,举个简单的例子,比如abcabcef,在访问到第四个a的时候发现和已经在hashmap中的第一个a重复,按照之前的做法,指针会直接跳回到位于第二个位置的b重新循环,改进后会继续访问位于第五个位置b,发现这个b也已经存在在了hashmap,而且这个b在hashmap中的位置2比之前记录的a在hashmap中的位置1靠后,所以更新记录b在hashmap中的位置,继续访问,c同样存在而且位置3比b靠后2,更新记录c的位置,e不存在在hashmap中,这时候跳回从记录的第三个c的下一个元素也就是第四个a重新遍历。
改进后的用javascript描述:
1 var lengthOfLongestSubstring = function(s) { 2 var pattern={}; 3 var i=0; 4 var longest_length=0; 5 var length=0; 6 var exist=-1; 7 while(i<s.length){ 8 if(s[i] in pattern){ 9 var dul_index=pattern[s[i]]; 10 exist=exist<dul_index?dul_index:exist; 11 i++; 12 } 13 else{ 14 if(exist!==-1){ 15 i=exist+1; 16 pattern={}; 17 length=0; 18 exist=-1; 19 } 20 else{ 21 pattern[s[i]]=i; 22 i++; 23 length++; 24 if(length>longest_length){ 25 longest_length=length; 26 } 27 } 28 } 29 } 30 return longest_length; 31 };
如果按照上面的方法,是可以通过的,但实际上有些开销是不必要的,像不断清空hashmap的操作能不能省略,也就有了再改进的方法,这个是参考discuss得出的,放上代码:
1 var lengthOfLongestSubstring = function(s) { 2 var pattern={}; 3 var i=0; 4 var start=0; 5 var max_length=0; 6 while(i<s.length){ 7 if(s[i] in pattern && start<=pattern[s[i]]){ 8 start=pattern[s[i]]+1; 9 } 10 else{ 11 max_length=max_length>(i-start+1)?max_length:(i-start+1); 12 } 13 pattern[s[i]]=i; 14 i++; 15 } 16 return max_length; 17 };
实际上就是把字符串中的每一个元素都存在了hashmap中,如果之前在hashmap中存在当前元素,就直接跟新在hashmap中的下标的值,然后通过start来控制范围,也可以得到结果。
使用C++代码如下:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 map<char,int> hashMap; 5 int max_length=0; 6 int start=0; 7 for(int i=0;i<s.size();i++){ 8 map<char,int>::iterator it=hashMap.find(s[i]); 9 if(it!=hashMap.end() && start<=hashMap[s[i]]){ 10 start=hashMap[s[i]]+1; 11 } 12 else{ 13 max_length=max(max_length,i-start+1); 14 } 15 hashMap[s[i]]=i; 16 } 17 return max_length; 18 } 19 };
Longest Substring Without Repeating Characters
标签:简单 去重 改变 判断 访问 char turn span 不重复
原文地址:http://www.cnblogs.com/yuruilee/p/6367383.html