【算法刷题】无重复字符的最长子串
本文为个人解题思路整理,水平有限,有问题欢迎交流
概览
第一次解出来没花多长时间,但是提交后发现击败了30%的人,也就是意味着还有大幅度优化的空间,于是再优化了一下
难度:中等
核心知识点:滑动窗口 + 贪心
题目来源
力扣:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
题目内容
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度
样例
输入1
pwwkew
输出1
3
解释1
最长无重复为
wke
解题思路
明显这是一个滑动窗口问题,即一个长度为L的窗口从左往右滑动,找到符合情况的最大L,下面进行思路分析
-
我们需要确定目标的长度,那么需要知道头head和尾tail,并确保中间的所有元素不重复
-
对于一个子串,如果存在重复元素,那么包含这个子串的任何其他子串也存在重复元素
-
贪心:即尽可能追求更多,若一个子串满足要求,那么考虑这个子串与相邻的字符能否组成更大的子串,进而将子串扩充,子串越大越好,因此得到的结果是对于每个起点,只有一个最远的终点,即每个起点对应最多一个结果
比如子串
p
符合要求,那么考虑pw
也符合要求,那么考虑pww
不符合要求,则已p
开头的最长子串是pw
,不可继续贪心 -
滑动:前面说了
pww
的最长子串是pw
,那么对于pww****
呢?*
代表暂时未知的字符,如果后面的结果比pw
长呢?那么我们也需要搜索后面的字符串,但是两个w
不能重复,于是不得不放弃一个w
,- 放弃后一个的结果就是
pw
- 放弃前一个的结果是
solve("w****")
,resolve
表示解决方法
也就是说
solve("pww****") = max(resolve("pw"), soleve("pw****"))
在对
soleve("pw****")
同样的方法处理下去,即可拿到最终的最大值比如对于目标字符串
abcadefa
,处理步骤如下(偷个懒,resolve省掉啦,不然看上去很乱)//第一次处理,两个a不同,必须放弃其中一个 solve("abcdadfa")=max(solve("abcd"),solve("bcdadfa")); solve("abcd")=4; //第二次处理,两个d不同 solve("bcdadfa")=max(solve("bcda"),solve("adfa")); solve("bcda")=4; //第三次处理,两个a不同 solve("adfa")=max(solve("adf"),solve("dfa")); solve("adf")=3; solve("dfa")=3; //那么最终结果为4 solve("abcdadfa")=max(4,max(4,max(3,3)))=max(4,4,3,3)=4
即从左往右贪心出最长的不重复字符串,直到出现一个与已有字符重复的目标,就贪不动了
这时候考虑放弃已有字符的和放弃新字符两种情况,取两种情况的结果最大的那个
- 放弃新字符,即当前子串的长度就是结果,我们一直在保证没有重复字符嘛
- 放弃旧字符,则从旧字符的下一位开始下一步检索,对新的目标重复贪心和处理
- 放弃后一个的结果就是
解决方案
-
设置两个指针 left 和 right ,初识均指向字符串的头部,设置初始最大长度
ans = 0
-
记录right目标的字符和位置,right 右移,直至其目标字符已出现过,开始处理
- 放弃新字符,则旧字符串的长度为
right-left
,修改ans = max(ans, right - left)
- 放弃旧字符,若旧字符位置的
oldPosition
,则 left 修改为oldPosition + 1
即得到新的字符串
- 放弃新字符,则旧字符串的长度为
-
right 继续右移,重复第二步
完整代码
这里使用数组存储位置,而非map,因为字符可按ASCII码存储,显然一个
int[128]
是比map的效率更高
package com.company;
import java.util.*;
/**
* @title 无重复字符的最长子串
* @description 力扣3
* @author Echo_Ye
* @date 2020/9/22 11:56
* @email [email protected]
*/
public class LengthOfLongestSubstring {
public static void main(String[] args) {
new LengthOfLongestSubstring();
}
public LengthOfLongestSubstring() {
int ans = lengthOfLongestSubstring("abcdadfa");
System.out.println(ans);
}
public int lengthOfLongestSubstring(String s) {
int ans = 0;
int left = 0, right = 0;
int[] map = new int[128];
Arrays.fill(map, -1);
while (right <= s.length()) {
if (right == s.length()) {
//结算,结束查找
ans = max(ans, right - left);
break;
}
//检查right目标
if (left == right || map[s.charAt(right)] == -1 || map[s.charAt(right)] < left) {
//存储位置
map[s.charAt(right)] = right;
//指针重叠的时候,右指针右移1
//右指针目标未出现过的时候,右指针右移1
right++;
} else {
//结算,left移至旧字符右1位置
ans = max(ans, right - left);
left = map[s.charAt(right)] + 1;
}
}
return ans;
}
int max(int a, int b) {
return a > b ? a : b;
}
}
结果
性能
后记
没有涉及什么高深的算法,就是思路灵活转换而已,好吧其实涉及到高级算法我可能就不会做了哈哈哈哈哈
作者:Echo_Ye
WX:Echo_YeZ
Email :[email protected]
个人站点:在搭了在搭了。。。(右键 - 新建文件夹)