无重复字符的最长子串
题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = ""
输出: 0
解题思路
当字符串为空或者长度为1时,直接返回字符串的长度
当字符串的长度大于1时,默认不重复字符串最大长度为1,采用两个指针 up 和 down 分别指向 字符数组的第一个、第二个位置
如果 up 和 down 指向的字符不相同,则 up 不动,down 向后移动,
否则,取出[up,down)之间的字符组成字符串,并同存储的不重复字符串最大长度比较,如果大于存储的不重复字符串最大长度,就更新其值,然后 down 指针不动,up向后移动,接着重复上述过程
移动过程中始终保持 down 指针 在 up指针的后面
代码
版本1.1
public class prac3 {
public static void main(String[] args) {
int len = lengthOfLongestSubstring("ckilbkd");
System.out.println(len);
}
public static int lengthOfLongestSubstring(String s) {
//如果字符串为空或为单个字符,直接返回字符串的长度
if (s.length() <= 1) {
return s.length();
}
//字符串的长度大于1的情况
//将字符串转为字符数组
char[] chars = s.toCharArray();
//定义两个指针,up,down
int up = 0;
int down = 1;
int max_str_len = 1;
String split_str = "" + chars[0];
while (down < s.length()) {
if (!split_str.contains("" + chars[down])) {
split_str = split_str + chars[down];
down++;
} else {
String temp = getStr(chars, up, down);
if(temp.length()>max_str_len){
max_str_len = temp.length();
}
up++;
if(up==down){
down++;
}
split_str = getStr(chars,up,down);
}
}
if(split_str.length()>max_str_len){
max_str_len = split_str.length();
}
return max_str_len;
}
public static String getStr(char[] chars, int i, int j) {
String str = "";
for (int k = i; k < j; k++) {
str += chars[k];
}
return str;
}
}
版本1.2
public class prac3 {
public static void main(String[] args) {
int len = lengthOfLongestSubstring("pwwkew");
System.out.println(len);
}
public static int lengthOfLongestSubstring(String s) {
//如果字符串为空或为单个字符,直接返回字符串的长度("","d")
if (s.length() <= 1) {
return s.length();
}
//字符串的长度大于1的情况
//将字符串转为字符数组
char[] chars = s.toCharArray();
//定义两个指针,up,down
int up = 0;
int down = 1;
int max_str_len = 1;
String split_str = "" + chars[0];
while (down < s.length()) {
if (!split_str.contains("" + chars[down])) {
split_str = split_str + chars[down];
down++;
} else {
String temp = getStr(chars, up, down);
if(temp.length()>max_str_len){
max_str_len = temp.length();
}
//"abcdc":down指针指向第二个'c'时,发现了重复,为了避免重复比较,采用
//此方式将 up指针 直接指向 'd'
up = down - (temp.length() - temp.indexOf(chars[down])) + 1;
//down指针和 up指针 有时会指向同一个字符
// 采用此方式始终保持 down 在 up 的后面,例子:("aaabcd")
if(up==down){
down++;
}
split_str = getStr(chars,up,down);
}
}
//针对没有重复字符串的情况(abcde)
if(split_str.length()>max_str_len){
max_str_len = split_str.length();
}
return max_str_len;
}
public static String getStr(char[] chars, int i, int j) {
String str = "";
for (int k = i; k < j; k++) {
str += chars[k];
}
return str;
}
}
大佬代码学习
思路
-
标签:滑动窗口
-
暴力解法时间复杂度较高,会达到 O(n^2),故而采取滑动窗口的方法降低时间复杂度
-
定义一个 map 数据结构存储 (k, v),其中 key 值为字符,value 值为字符位置 +1,加 1 表示从字符位置后一个才开始不重复
-
我们定义不重复子串的开始位置为 start,结束位置为 end
-
随着 end 不断遍历向后,会遇到与 [start, end] 区间内字符相同的情况,此时将字符作为 key 值,获取其 value 值,并更新 start,此时 [start, end] 区间内不存在重复字符
-
无论是否更新 start,都会更新其 map 数据结构和结果 ans。
-
时间复杂度:O(n)
-
作者:guanpengchn
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/hua-jie-suan-fa-3-wu-zhong-fu-zi-fu-de-zui-chang-z/来源:力扣
代码示例
class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>();
for (int end = 0, start = 0; end < n; end++) {
char alpha = s.charAt(end);
if (map.containsKey(alpha)) {
start = Math.max(map.get(alpha), start);
}
ans = Math.max(ans, end - start + 1);
map.put(s.charAt(end), end + 1);
}
return ans;
}
}
个人感受:感觉大佬这个算法还是挺好的,但是不好理解,我觉得存储字符下标更容易理解,在理解大佬的思想后,自己又写了一份代码,感觉确实优化了不少。
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
int max_len = 0;
HashMap<Character,Integer> map = new HashMap<>();
for (int end=0,start=0; end < len; end++) {
char alpha = s.charAt(end);
if(map.containsKey(alpha)){
//原先自己的思路是采用 start = map.get(alpha) + 1 来
//更新start指针的位置,后来发现有问题
//在测试字符串 "abba" 时,当 end 指向 最后一个 'a' 时,发现重复
//于是将 start 更新为 第一个字符'a'的位置,这就增大了最大无重复字符串的长度
start = Math.max(map.get(alpha) + 1,start);
}
map.put(alpha,end);
max_len = Math.max(max_len,end - start + 1);
}
return max_len;
}
}
- 测试案例: "" "a" "aaaa" "aaabbw" "pwwkew" "abcabcbb" "abba"
- 感觉力扣给出的测试案例还是挺好的,通过案例很容易发现问题并改进自己的代码,这道题感觉自己想复杂了,字符串长度完全可以通过两个指针来得到,自己却先将其截取,再求长度,使问题变得更复杂,HashMap真是好用,怪不得面试总爱问 hashMap 的 源码,受教了~
感想:第一次做力扣的算法题,感觉还是挺难的,一方面自己思路不清晰,另一方面容易少考虑情况,提交了好几次都是错的~,代码还是使用笨的方法实现的,这道题的类型属于滑动窗口,明天准备学习下大神的代码,并学习下左神算法的视频,以后要坚持刷算法题