无重复字符串的最长子串(有点难)
题目链接:无重复字符串的最长子串
题干内容:
-
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度。 -
示例一:
输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
-
示例二:
输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
-
示例三:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成
尝试解法(请跳过):
public int lengthOfLongestSubstring(String s){
int max = 0;
for (int i = 0; i < s.length(); i++) {
String str = s.substring(i);
max = Math.max(max , getNum(str));
}
return max;
}
public int getNum(String s) {
int time = 0;
int length = 0;
char[] chars = s.toCharArray();
if (s.length() == 1){
return 1;
}else if (s.length() == 0){
return 0;
}
for (int i = 0; i < chars.length; i++) {
String s1 = s.substring(0,i);
length = i;
if ( s1.contains(chars[i]+"") ){
time++; //表示有重复字符串
//System.out.println(i+"______"+s.substring(i+1));
int temp = lengthOfLongestSubstring( s.substring(i) );
if (temp > length){
length = temp;
System.out.println("交换:"+temp);
}
break;
}
}
//System.out.println("返回:"+length);
return time==0 ? length+1 : length;
}
直接超出时间限制,简单来说,就是时间复杂度太大,代码写得太垃圾了。
简单思路:
-
设置一个变量length记录当前最长子串的长度;两个变量i,j分别记录下标位置。
String str = "abcdcabed"; int length = 0; int i,j = 0;
-
从首字符开始遍历,每遍历一个不相等字符使length++,j++,i不变;(即:记录每一次遍历后的最长长度。)
-
直到遇见已经遇见过的字符比如字符’c’,则从已遍历的字符中寻找下标最大的那个’c’;
"abcdcabed" j指向第二个'c',j=4 ,i=0不变 此时length = 4,j=4;因为遍历到第二个字符'c'时,因为已存在,所以length不加,所以length = 4为目前的最长长度
-
以此字符’c’为临界点拆分原来的字符串,以此字符’c’之后的字符串为新的字符串,i指向此字符’c’之后的字符。
"abcdcabed" 拆分后:"abc" "dcabed" j仍指向第二'c':j=4; i指向第一个'c'后面的字符'd', i=3;
-
计算新的长度length,与之前的length相比较取大的数。
"dcabed" 依次为新的字符串来看,相当于j的第二次"j++"; 所以新length = j-i+1=2;
-
循环步骤2,直至遍历完整个原字符串。
解法一
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int length = 0;
Map<Character, Integer> map = new HashMap<>();
for (int j = 0, i = 0; j < n; j++) {
//有重复则移动i的下标
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
length = Math.max(ans, j - i + 1);//设置新长度
map.put(s.charAt(j), j + 1);//key值为记录字符,value记录当前字符下角标的后一位,用来更改i节点
}
return length;
}
}
-
时间复杂度:O(n)。
-
空间复杂度:O(min(m,n))。
-
执行用时:4 ms, 在所有 Java 提交中击败了86.62%的用户
-
内存消耗:41.2 MB, 在所有 Java 提交中击败了67.41%的用户
通过测试用例:987 / 987
解法二
将每次读取的字符的 “下标+1" 存到整型数组index中去(以字符对应的ASCLL码值为下标)
缺点是只是用字符串长度不那么大的字符串。
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), length = 0;
int[] index = new int[128];
for (int j = 0, i = 0; j < n; j++) {
//自动判断了是否遇见已存在的字符,
//因为若字符已存在,则index[s.charAt(j)]返回对应i变化后的下标;
//若字符不存在,则index[s.charAt(j)]本身就是0。
i = Math.max(index[s.charAt(j)], i);
length = Math.max(length, j - i + 1);
index[s.charAt(j)] = j + 1; //将对应i的下标存到以字符ASCLL值为下标的位置去
}
return length;
}
}
时间复杂度:O(n)。
空间复杂度:O(m),m 代表字符集的大小。这次不论原字符串多小,都会利用这么大的空间。
执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:41.2 MB, 在所有 Java 提交中击败了64.24%的用户
通过测试用例:987 / 987