我的LC之路-3. Longest Substring Without Repeating Characters

Posted on 2017-11-06 15:03  泫辰  阅读(128)  评论(0)    收藏  举报

写在前面:在LeetCode上做的题,代码基本都是我自己写的,效率有好有坏,仅供参考。
有少数几道题苦思冥想做不出来的,可能会借鉴网上大神的解法,然后再自己编写,借鉴过他人解法的题会有说明。
本人不是算法出身,语言基本靠自学,有任何错误理解或者不当举措,还请各位大侠手下留情并不吝赐教!万分感谢~

题:

3. 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.

解题思路:

做这道题中间空了挺久的,一度想不出好的解法而搁置在一旁。前两天又重新看了看题,突然灵光一闪,居然做出来了,真是心情舒畅~

之前卡着,也一度是因为没有好的解法,拿到题后做了最笨的打算:双循环~通过检验所有子串,找到最长的那条无重复字符的子串...代码如下:

 1 public class Solution {
 2     public int lengthOfLongestSubstring(String s) {
 3         int k=0,max = 0;
 4         String ss = new String();
 5         boolean g = false;
 6         for(int i=s.length()-1;i>0;i--){
 7             for(int j = 0;j<s.length()-i;j++){
 8                 ss = s.substring(j,j+i+1);
 9                 if(g = judge(ss)) return ss.length();
10             }
11         }
12         return 0;
13     }
14     public boolean judge(String s){
15         String ss = new String();
16         for(int i = 0;i<s.length();i++){
17             if(ss.contains(s.substring(i, i+1))) return false;
18             else ss+=s.substring(i, i+1);
19         }
20         return true;
21     }
22 }

各位大侠看了估计乐了,这么蠢的做法~没错,是挺蠢的,但就像我一贯的做法,先做出来,再寻找好解法。先得给自己找到立足之地不是~

然而这次的代码虽然在本地机器上测试了很多数据,结果也都是正确的。(当然了。。。一个一个试还能有错么。。。)

提交之后,显而易见的Time Limit Exceeded。。。鲜艳而明亮的超时。。。

当时也是脑子不太灵光,一时找不到不用双循环的解法。。。于是干脆放弃。。。

等到已经完全忘了这题时,再打开重新开始做,没有了上次解法的禁锢,往往能够另辟蹊径。

从题干中看到,需要非重复的子串,先想到的是利用hashmap没有重复key的特性,进行字符的存储。经过一番测试,发现不是很好操作,也就是在调试的过程中,突然想到可以用队列,从而进一步想到,可以将字符串转换成字符数组,然后在下标上做文章。

并自己编写类似的contains类,达到相应的功能。代码如下:

 1 class Solution {
 2 
 3     public int start = 0;
 4     public int end = 0;
 5 
 6     public int lengthOfLongestSubstring(String s) {
 7         int result = 0;
 8         char[] c = s.toCharArray();
 9         while(end<s.length()){
10             if(!this.contains(c,start,end,c[end])){
11                 end++;
12                 if(result<end-start) result++;
13             }else{
14                 this.remove(c, c[end]);
15                 end++;
16             }
17         }
18         return result;
19     }
20 
21     public void remove(char[] c,char target){
22 
23         while(start!=end){
24             if(c[start]==target){
25                 start++;
26                 break;
27             }
28             start++;
29         }
30     }
31 
32     public boolean contains(char[] c,int start,int end,char target){
33 
34         while(start<=end){
35             if(c[start]==target&&start!=end) return true;
36             start++;
37         }
38         return false;
39     }
40 }

代码中两个方法:

remove:将target字符从字符数组中移除,具体想法下面会有详细解释。

contains:类似于String的contains方法,判断字符数组中是否存在target这个字符,查找范围是从下标start到end。

这两个方法contains比较容易理解,remove可能会让人有点云里雾里。别急,且听我娓(zhaung)娓(qiang)道(zuo)来(shi)~

其实整体的想法很简单,从第一个字符开始往下找,end下标不断后移,每移动一次都往前进行对比,看看从start到end范围内(这个范围其实就是最长子串,通过result记录运行过程中出现的最长的结果)是否存在该字符,如果存在,则将start向后移动,直到将重复出现的字符移出范围。(感觉跟没解释一样啊!!!)举个例子...:abcdebfgdc

这个字符串的最长子串应该是6(cdebfg)。处理过程为:

最开始start和end都为0,指向第一个字符a,然后end开始向后移动。当end移动到5时,指向了第二个b的位置,这是,根据contains返回的结果会发现, 从start(0)到end(5)中,已经存在b这个字符了,这时开始移动start,start移动到2之后,第一个b字符被排出。然后end继续向后走(result同时记录当前遇到过的最大的子串),走到8时,找到第二个d,这时又开始移动start以此类推...

最后,得到中间出现过的cdebfg长度为6的这个答案。

提交之后,运行时间44ms

效率还算不错~满满的成就感~哈哈~