3.Longest Substring Without Repeating Characters

题目连接:https://leetcode.com/problems/longest-substring-without-repeating-characters/description/java

题目大意:找出一串字符串的最长无重复子串。例子以下:数组

法一:两层for循环,一个字符一个字符的遍历其后是否有子串,且要判断是否不重复。这里有几个细节点,map的使用判断是否有重复,string.charAt(index)的使用摘出某一个字符。代码以下(耗时206ms):ide

 1     public int lengthOfLongestSubstring(String s) {
 2         int length = 1;
 3         int max = 0;
 4         for(int i = 0; i < s.length(); i++) {
 5             length = 1;
 6             Map<Character, Integer> map = new HashMap<Character, Integer>();
 7             map.put(s.charAt(i), i);
 8             for(int j = i + 1; j < s.length(); j++) {
 9                 if(map.containsKey(s.charAt(j)) == false) {
10                     //若是前面的字符串中没有当前字符,则将当前字符加入字符串中
11                     length++;
12                     map.put(s.charAt(j), j);
13                 }
14                 else {
15                     //若是有,则直接退出从当前字符串起始位置的下一个位置开始从新计算字符串
16                     //这里就有优化的地方了
17                     break;
18                 }
19             }
20             if(length > max) {
21                 max = length;
22             }
23         }
24         return max;
25     }
View Code

法二(借鉴):一层for循环,用left记录子串起始位置,用set判断是否有重复,每走到下一个下标位置,判断当前存的子串中有没有当前字符,若是有,则根据left++将重复字符前面的包括它本身都从set中删去,也就是将重复字符+1做为新的子串的起始位置;若是没有,则将当前字符加入子串中。有点kmp的思想,这样只须要一次for循环便可。代码以下(耗时70ms):优化

 1     public int lengthOfLongestSubstring(String s) {
 2         int length = s.length();
 3         int left = 0, right = 0, ans = 0;//left记录子串开始下标,right记录子串结束下标
 4         HashSet<Character> set = new HashSet<Character>();//set判断重复字符
 5         while(right < length) {
 6             if(set.contains(s.charAt(right)) == false) {
 7                 //前面的字符串中不包含当前字符
 8                 //将当前字符加入子串中,长度+1
 9                 set.add(s.charAt(right++));
10                 ans = Math.max(ans, set.size());
11             }
12             else {
13                 //前面的字符串中包含当前字符
14                 //将重复字符前面的包括它本身都删去,也就是从重复字符+1开始从新计算子串
15                 set.remove(s.charAt(left++));
16             }
17         }
18         return ans;
19     }
View Code

法三(借鉴):法二须要遍历两遍,这个方法只须要遍历一遍,优化的地方在于:若是当前存的子串中有当前字符,则将left直接跳到重复字符下标+1处,而无需像法二同样从left处一个一个的往下跳。那么这里也就涉及到怎么保存重复字符下标的问题,很容易想到用map来作。的确用map能够解决,可是还有一点注意就是要跳到重复字符下标+1处,而不单单是跳到重复字符处,因此这里在何处+1应该谨慎,好比下面这样一个例子:spa

字符串“pwkkfw”:code

第一趟:left = 0, right = 0, ans = right - left + 1 = 1, map(p, right + 1 = 1)blog

第二趟:left = 0, right = 1, ans = right - left + 1 = 2, map(p, 1)(w, right + 1= 2)ip

第三趟:left = 0, right = 2, ans = right - left + 1 = 3, map(p, 1)(w, 2)(k, right + 1 = 3)leetcode

第四趟:left = map(k).value = 3, right = 3, ans = 3, map(p, 1)(w, 2)(k, right + 1 = 4)rem

第五趟:left = 3, right = 4, ans = 3, map(p, 1)(w, 2)(k, 4)(f, right + 1 = 5)

第六趟:left = 3[这里不是2,由于要取大者,并且也取不到2,由于中间存在重复字符,取不到该字符串], right = 5, ans = 3, map(p, 1)(w, right + 1 = 6)(k, 4)(f, 5)

代码以下(耗时55ms):

 1     public int lengthOfLongestSubstring(String s) {
 2         Map<Character, Integer> map = new HashMap<Character, Integer>();
 3         int ans = 0;
 4         int length = s.length();
 5         for(int left = 0, right = 0; right < length; right++) {
 6             if(map.containsKey(s.charAt(right)) == true) {
 7                 //若是所存字符串中有当前字符,更新子串起始位置
 8                 //很容易犯错成left = map.get(s.charAt(right))+1,这里不能直接将最新值当成子串起始位置
 9                 //由于有可能这个最新值已是过时值,即有多是比当前left小的下标,而其后已经有重复字符不能取,好比pwkkfw字符串
10                 //这里也不能犯错成left = Math.max(left, map.get(s.charAt(right)))
11                 //由于要取到重复字符下标+1的位置,固然这里若是下面是map.put(s.charAt(right), right + 1)则就能够写成left = Math.max(left, map.get(s.charAt(right)))
12                 left = Math.max(left, map.get(s.charAt(right))+1);
13             }
14             ans = Math.max(ans, right - left + 1);
15             map.put(s.charAt(right), right);
16         }
17         return ans;
18     }
View Code

法四(借鉴):思想相同,用int数组换掉map。代码以下(耗时39ms):

 1     public int lengthOfLongestSubstring(String s) {
 2         int ans = 0, length = s.length();
 3         int[] flag = new int[256];
 4         for(int left = 0, right = 0; right < length; right++) {
 5             //这里不能用flag[s.charAt(right)+1,由于java默认初始化是0,这里若是+1,就会致使起始位置+1状态
 6             //若是前面初始化为-1了,这里就能够+1,下面也能够改为flag[s.charAt(right)] = right
 7             left = Math.max(left, flag[s.charAt(right)]);
 8             ans = Math.max(ans, right - left + 1);
 9             flag[s.charAt(right)] = right + 1;
10         }
11         return ans;
12     }
View Code
相关文章
相关标签/搜索