题目:
给定一个字符串,找出不含有重复字符的 最长子串 的长度。
示例:
给定 "abcabcbb"
,没有重复字符的最长子串是 "abc"
,那么长度就是3。
给定 "bbbbb"
,最长的子串就是 "b"
,长度是1。
给定 "pwwkew"
,最长子串是 "wke"
,长度是3。请注意答案必须是一个子串,"pwke"
是 子序列 而不是子串。
方法一:
不重复可以用List的contains(object obj)实现,后面就很简单了,定义max为最长字串长度,初值为0。从起点开始不断扫描,contains(obj)返回false就将其加入List,返回true就计算List长度,与max比较。接下来将起点+1同时clear()掉List,继续这个过程即可,代码就不贴了,比较简单。出乎意料,这样的暴力法也AC了,但是用时很长(超过5%的AC代码)。
方法二:
接下来就是想如何去优化它。
- 不重复可以用哈希表实现,因为ASCII码只有256个(题目中用到更少),只要定义一个大小为256的boolean数组hash,有字符c则将hash[c]置为true。
- List也是而安全可以不用的,以为hash数组已经记录了List中的内容,我们只需另外定义一个整数window(可以理解为窗口)作为方法一中List的长度,当出现重复时将max与window大者赋给max即可。
- 在方法一中将List清空相当于将之前的工作全部作废了,其实可以保留部分。例如,对于串
"bacdaea"
,进行到第二个 a 时不必将hash数组全部置为false,只要将第一个a前面的元素对应位置置为false(即hash[(int)b]=false),同时调整当前窗口大小即可。
我用串"bacdaea"
进行举例,初始时 window=0,,max=0,hash数组全为false。
遍历字符串,从i=0到i=3,window和max都逐步增加到4,hash数组下标为“b","a","c","d"的 boolean 值都为true,不重复的字串为"bacd"。
i = 4,此时扫描到"a",hash[a]=true,则从i-window=0开始寻找第一个"a",找到位置记作start=1,遍历 [i-window,start) 区间的字符charc,令hash[charc]=false,最后将window调整为i-start=3。此时不重复的字串为”cda"。
i = 5,"e"不重复,置hash[(int)e]=true,window变为4,不大于max,max不变,此时字串为"cdae".
i = 6,类似i=4时,start=4,window变为2,到这里原字符串就遍历完了,最后返回max=4即可。代码如下:
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s == null ||s.length() < 1)
return 0;
boolean[] hash= new boolean[256];
int max = 0;
int window = 0;//窗口大小
for(int i=0;i<s.length();i++){
if(hash[s.charAt(i)]){//重复了
//从i-window开始找,找到重复字符
int start = s.indexOf(s.charAt(i), i-window);
//窗口推进到重复字符后
for(int j=i-window;j<start;j++){
hash[s.charAt(j)] = false;
}
//缩小窗口(大小一定是缩小或者不变,所以不必和max比较)
window = i-start;
}
else{
hashMap[s.charAt(i)] = true;
if(++window > max)
max = window;
}
}
return max;
}
}
我的代码运行时间是36ms,超过91%AC代码。然而最快的代码只需要26ms,我看完后发现与我的思路相似,但是去掉了不必要的步骤。
方法三:
仔细观察字符串"abcdacebde"
,当我们扫描到第二个字符"a"时,我们发现了重复的"a",自然的窗口移动到“a"后,变为"bcda",再移到"c"时,同样窗口移动为"dac",可以发现我们只要比较窗口中的所有字符与当前字符(第i个)是否相等就好了,无重复则窗口+1,有重复则窗口移动,即:窗口右端恒定+1,左端不移动或者移动到重复字符之后。
这样分析就知道了hash数组是没有必要的,我们只要定义窗口的左右left和right就可以到我们的目的。我们只要从窗口左端left向右挨个字符和第i个字符比较,如果都不等,则left不变;如果找到一个charAt(j) = charAt(i),令left = j+1即可。代码如下:
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s == null || s.length() == 0)
return 0;
int longest = 1;
int left = 0, right = 1;
while(right <= s.length() - 1) {
char digit = s.charAt(right);
for(int i = left; i < right; i++) {
if(digit == s.charAt(i)) {
left = i + 1;
break;
}
}
longest = Math.max(longest, right + 1 - left);
right++;
}
return longest;
}
}
友情链接,软安同学的博客 -->ma5ker