第三道题,和前面的那个第一道题数组里面的两个元素求和有点像,题目如下:

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来控制范围,也可以得到结果,也就是使用start<pattern[s[i]]这个判断条件来充当前面的exist的作用,这样也就不用清空hashmap了。

使用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 };

 

总结C++知识点:

1)有关max函数,在判断并返回两个数中较大的那个,可以直接用max函数,比如max(1,2)就直接会返回2

2)有关字符串的三个操作的区别,size(),length(),capacity(),用一个形象的比较,capacity()返回的是事先声明的字符串的容量,size()和length()返回的是字符串的实际长度,就像有一个空的背包,这个背包的整个容量就是capacity,这个背包中能装10本书,这10本书就是capacity,实际上这个背包中只放了2本书,这两本书就是size()和length()

 

 posted on 2017-02-05 13:08  yuruilee  阅读(204)  评论(0编辑  收藏  举报