题目描述
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew” 输出: 3 解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
解题思路
动态规划法
:推导状态转移方程:给出一个字符串Si,已知它的最长子串长度为Li,如果在字符串Si的末尾追加一个字符C,即Si + 1 = Si + C
,那么Si + 1
的最长字串变为多少- 可以考虑出状态转移方程为
lengthOfLongestSubstring(Si + 1) = max(Li,len(以C结尾的无重复子串))
- 编写一个
find_left
函数用来寻找无重复子串长度
Python实现
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if s == '':
return 0
if len(s) == 1:
return 1
def find_left(s, i):
tmp_str = s[i]
j = i - 1
while j >= 0 and s[j] not in tmp_str:
tmp_str += s[j]
j -= 1
return len(tmp_str)
length = 0
for i in range(0,len(s)):
length = max(length,find_left(s,i))
return length
- 时间复杂度:O(n^2)
解题思路
滑动窗口法
:从字符串左侧开始遍历,以i
标记窗口左侧,j
标记窗口右侧,初始时,i=0
,j=0
,此时,窗口的大小为1,然后将j
右移,逐步扩大窗口,窗口内均无重复字符,j
持续右移,i保持不变,当j
移动到窗口中已存在的字符时,去除当前重复的一位,找到窗口中已存在的该字符所在位置,并将i
移动到该位置的下一位,此时得到第二个窗口,重复操作,直到j
移动到字符串最后一位停止- 我们以字符串
a
bcda
fg为例,i=0,j=0,即a
所在的位置,此时窗口大小为1,之后j
不断右移,直到再次经过a
,此时出现了窗口内的字符,即把i
移动到下一位即b即可,之后再去移动j
,直到j
移动到最后一个字符串停止,即无重复字符的最长子串为:bcdafg
Cpp实现
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int> window;
int left = 0,right = 0;
int res = 0;//记录结果
while(right < s.size()){
char c = s[right];
right++;
window[c]++;//对窗口进行更新
while(window[c] > 1){
char d = s[left];
left++;
window[d]--;
}
res = max(res,right - left);
}
return res;
}
};
- 时间复杂度:O(n),只遍历了一遍字符串
Cpp实现
- 通过观察题解,发现一种很漂亮的解法,也记录在自己的blog中,学习一下
Hashmap
:建立每个字符和其最后出现位置之间的映射,res用来记录最长无重复子串的长度,left
指向改无重复子串左边起始位置的前一个Hashmap
:ASCii表有256个字符,将所有位置初始为-1,遍历字符串- 结合滑动窗口写出代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int>m(256,-1);
int left = -1;
int res = 0;
int len = s.size();
for(int i = 0;i<len;i++){
left = max(left,m[s[i]]);
m[s[i]] = i;
res = max(res,i - left);
}
return res;
}
};
- 时间复杂度:O(n)