原题描述
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
示例 4:
输入: s = “”
输出: 0
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
一题多解
解法一(最原始的暴力)
思路:
首先想到就是双指针法,一个指针指向子串的头,一个指向子串的尾。
1、开始时头指针h 指向字符串的首字符,尾指针 t指向字符串的第二个字符。还需要借助一个指针p用来访问子串内的元素。
2、固定一个子串后,需要用p遍历子串的每一个元素并与子串的最后一个字符做比较,以确定子串是否是不重复的子串。
3、子串内无重复的字符,则当前以头指针位置为起点的无重复子串长度加1(但不确定其他起点的无重复子串的最大长度是多少,需要打擂确定),尾指针加1,指针p重置回到头指针的位置,重复步骤2;子串内有重复的字符,则尾指针不动,头指针加1,指针p重置回到头指针位置。重复步骤2和3,直到尾指针来到字符串的末尾。
解法二(好一点的优化)
思路:
优化解法一的步骤三:当子串发现重复元素的时候,头指针是不是不需要一个位置一个位置地向前移动呢?KMP算法给了我启发,在重复的子串中,从头指针到重复元素所在的位置(也就是指针p指向的位置),这一段字符串中的任一子序列,均不可能与当前子串的最后一个字符构成一个不重复的子串,因为这些子串必定包含了两个重复的字符(指针p指向的元素和尾指针指向的元素)。因此,当子串发现重复元素的时候,我们不需要把头指针一次一次向前加1,而是可以直接把头指针移到指针p指向的元素的后面一个元素。这样就可以减少头指针移动的次数,从而降低时间复杂度。
代码:
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s.equals(""))//空串直接返回长度为0
return 0;
int h=0;//h->子串头
int t=h+1;//t->子串尾
int p=0;//p->当前子串中待检查元素的位置
int max=1;
char temp='0';
while(t<s.length()){
/*遍历当前子串,看有没有与子串中最后一个字符重复的 */
temp=s.charAt(t);
while(p<t &&s.charAt(p)!=temp){
p++;
}
/*有重复 */
if(p<t){
h=p+1;//把子串左端滑到重复的位置后面
}
/**没重复 */
else{
if(t-h+1>max)//打擂维护最大子串长度
max=t-h+1;
}
p=h;
t++;
}
return max;
}
}
解法三(看题解后我认为比较好的方法)
思路:
数据结构:滑动窗口–双指针的形象化思考(便于代码模板化)
定义:一个自定义的满足某个性质的队列(本题滑动窗口是一个只包含无重复字符的字符串的队列)
基本操作:1、入队 2、出队
怎么维护?
1、入队(队尾指针加1)
2、判断队列前面元素是否要出队(通过判断当前队尾是否与队列其他元素相同(可以遍历队列,也可以借助哈希集合,如java中的HashMap) )
3、判断成立,则n个元素出队(队头指针加n)
在遍历一个字符串的过程始终维护这样一个滑动窗口(重复1、2和3步骤)
代码:
在这里插入代码片