大家好,我是 ​陌溪​ 。

今天呢,给大家带来字节的一道面试真题, ​求无重复字符的最长子串

全文引入虚拟人物 小笨猪阿土 和 小美妞阿梅,通过 ​漫画+动图​​​ 的风格让关注我的读者轻松、愉快的学习算法,毕竟

被字节的这道算法题面哭了_数据结构

故事

今天,小笨猪 ​阿土​ 收到了字节跳动的一面邀请邮件,约定3天后面试。

这可把 ​阿土​ 高兴坏了,但是 ​阿土​的算法水平比较差,他听说字节跳动每轮面试必考算法,所以心情很忐忑。

这个时候,他的好朋友小美妞 ​阿梅​ 来找她玩耍,看到小笨猪闷闷不乐,于是问起了缘由,当得知字节跳动每轮都考算法的时候,阿梅说,不要担心,​我们现在就开始刷代码​,正好我今天刚做了一道题,可以考考你。

被字节的这道算法题面哭了_字符串_02

题目描述:

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例:
输入: s = "abcabcbb"
输出: 3

输入: s = "bbbbb"
输出: 1

阿土看了看题目,这个?想了好几分钟,没想出答案。

被字节的这道算法题面哭了_字符串_03

题目解析

阿梅​看到小笨猪一直没有想出来,于是开始给小笨猪讲解:

1、题目思路

这道题主要用到思路是:​​滑动窗口​

什么是滑动窗口?

其实就是一个队列,比如例题中的 ​​pwwkew​​​,进入这个队列(窗口)为 ​​pw​​​ 满足题目要求,当再进入​​w​​​,队列变成了 ​​pww​​,这时候不满足要求。所以,我们要移动这个队列!

如何移动?

我们只要从队列的左边开始将元素移出,直到满足题目要求!

一直维持这样的队列,找出队列出现最长的长度时候,求出解!

时间复杂度:O(n)

2、步骤

  1. 可以定义一个​​map​​​数据结构存储​​(k,v)​​​,其中​​key​​​为字符,​​value​​为字符的下标;
  2. 我们定义不重复子串的开始位置为​​start​​​,结束位置为​​end​​​.​​start​​​ 刚开始不动,​​end​​向后移动;
  3. 当​​end​​​遇到重复字符时,将​​start​​​放在上一个重复字符位置的后一位,同时记录最长的长度​​max​​;
  4. 通过​​hashmap​​​的​​key​​​来判断重复字符,用​​value​​来记录该字符的下一个不重复的位置。

刚开始,​​start​​​ 和 ​​end​​​ 都处于下标​​0​​​, ​​max​​​初始定义为​​0​​​,​​hashmap​​​中没有存放​​key​​​ 和 ​​value​​​。被字节的这道算法题面哭了_字符串_04

通过​​for​​​循环,从​​end​​​为​​0​​​ 开始遍历,判断第​​1​​​个字符​​p​​​,​​hashmap​​​中没有包含​​p​​​,这时将​​p​​​作为​​key​​​存入​​map​​​,下标​​0​​​存入​​value​​​中。​​max =Math.max(max,end-start +1) =1​​,

继续执行,​​end = end+1 = 1​​如下图:

被字节的这道算法题面哭了_数据结构_05

判断第​​2​​​个字符​​w​​​,​​hashmap​​​中没有包含​​w​​​,这时将​​w​​​作为​​key​​​存入​​map​​​,下标​​1​​​存入​​value​​中。

​max =Math.max(max,end-start +1) =2.​

继续执行, ​​end = 1+1 = 2​​​。如下图:被字节的这道算法题面哭了_数据结构_06

判断第3个字符​​w``,hashmap​​​中包含​​w​​​,这时计算​​start​​​,将​​start​​​放在上一个重复字符位置的后一位,即上一个​​w​​​下标为​​1​​​的后一位,所以​​start = Math.max(start,map.get(ch)+1)=2。​

然后在​​map​​​中更新​​w​​​下标为​​2​​​,重新计算​​max = Math.max(max,end-start +1) =2​​​, 继续执行,​​end = 2+1 = 3​​。如下图:

被字节的这道算法题面哭了_字符串_07判断第​​​4​​​个字符​​k​​​,​​hashmap​​​中没有包含​​k​​​,这时将​​k​​​作为​​key​​​存入​​map​​​,下标​​3​​​存入​​value​​​中。​​max =Math.max(max,end-start +1) =2​​​,继续执行,​​end = 3+1 = 4​​。如下图:

被字节的这道算法题面哭了_字符串_08

判断第​​5​​​个字符​​e​​​,​​hashmap​​​中没有包含​​e​​​,这时将作为​​key​​​存入​​map​​​,下标​​4​​​存入​​value​​​中。​​max =Math.max(max,end-start +1) =3​​​, 继续执行,​​end = 4+1 = 4​​​。如下图:被字节的这道算法题面哭了_数据结构_09

判断第​​6​​​个字符​​w​​​,​​hashmap​​​中包含​​w​​​,这时计算​​start​​​,将​​start​​​放在上一个重复字符位置的后一位,即上一个​​w​​​下标为​​2​​​的后一位,所以​​start = Math.max(start,map.get(ch)+1)=3​​。

然后在​​map​​​中更新​​w​​​下标为​​5​​​,重新计算​​max = Math.max(max,end-start +1) =3​​​, 继续执行,​​end = 5+1 = 6​​​。如下图:被字节的这道算法题面哭了_数据结构_10

判断​​end=6​​​时,超出字符串范围,程序结束,所以最终字符的长度为​​3​​。

3、动画如下:

被字节的这道算法题面哭了_子串_11被字节的这道算法题面哭了_字符串_12被字节的这道算法题面哭了_字符串_13

代码实现

java实现

class Solution {
public int lengthOfLongestSubstring(String s) {
// (1) 先创建map数据结构存储(k,v),其中key为字符,value为字符的下标.
HashMap<Character,Integer> map = new HashMap<>();
//(2)定义不重复子串的开始位置为start,结束位置为end. start 刚开始不动,end向后移动
int max = 0 , start = 0;
//(3) 遍历字符串
for(int end = 0 ; end < s.length() ; end++){
// (4)取出每个字符
char ch = s.charAt(end);
// (5)判断字符,hashmap中没有是否包含该字符
if (map.containsKey(ch)){
// (6)包含字符,将start放在上一个重复字符位置的后一位。
start = Math.max(start,map.get(ch)+1);
}
// (6)计算max长度
max = Math.max(max,end -start+1);
//(7)将字符作为key存入map,下标存入value中
map.put(ch,end);

}
// (8)程序执行结束,返回无重复子串最大长度。
return max;
}
}

c++实现

class Solution {
public:
int lengthOfLongestSubstring(string s) {
if(s.size() == 0) return 0;
unordered_set<char> lookup;
int maxStr = 0;
int left = 0;
for(int i = 0; i < s.size(); i++){
while (lookup.find(s[i]) != lookup.end()){
lookup.erase(s[left]);
left ++;
}
maxStr = max(maxStr,i-left+1);
lookup.insert(s[i]);
}
return maxStr;

}
};

python3实现

class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:return 0
left = 0
lookup = set()
n = len(s)
max_len = 0
cur_len = 0
for i in range(n):
cur_len += 1
while s[i] in lookup:
lookup.remove(s[left])
left += 1
cur_len -= 1
if cur_len > max_len:max_len = cur_len
lookup.add(s[i])
return max_len

被字节的这道算法题面哭了_子串_14

Rita​ 的代码均已经通过​ Leetcode​ 在线测评,保证代码是没有问题。