无重复字符的最长子串
https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/
题目描述
题目分析
寻找无重复字符子串,首先要求是子串,然后是无重复
子串可以用滑动窗口确定
问题在于如何确定无重复
如果用暴力枚举子串,复杂度近似
O
(
n
3
)
O(n^3)
O(n3)是不可接受的
所以我们必须降低复杂度,而暴力枚举的问题就在于首指针的每次移动后窗口都要重新开始扩大,而对于每个窗口我们都要遍历每一个元素以确定无重复。
题目解决
首先,我们假设滑动窗口内的子串一定是无重复的,每当我们移动尾指针进行扩大时,只有当被扩充的元素是非重复的才可以扩大,否则移动首指针直到被扩充的元素不在是重复的。
这样确实降低了复杂度,但也引入了新的问题——如何维护这样一个无重复元素的子串窗口
我们采用计数表示的方法,因为我们不考虑子串中的元素是如何分布的,只在意元素的种类和数量,所以我们可以特化相关特征,并用于表示该子串
代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> window;
int ans = 0;
int j = 0, slen = s.length(), i = 0;
if(slen == 1) return 1;
while(j < slen){
auto it = window.find(s[j]);
if(it == window.end()){
window[s[j]] = 1;
if((j - i + 1 ) > ans){
ans = (j - i + 1 );
}
}
else if(it->second == 1){
while(window[s[j]] && i < j){
--window[s[i]];
++i;
}
++window[s[j]];
}
else{
window[s[j]] = 1;
if((j - i + 1 ) > ans){
ans = (j - i + 1 );
}
}
// printf("%d %d %d %d\n",i, j, window[s[j]], window[s[i]]);
++j;
}
return ans;
}
};
Abstract
设
s
i
s_i
si 为子串中的元素,
f
(
s
i
)
f(s_i)
f(si) 为
s
i
s_i
si 所表示的字符
无重复字符串表示如下
S
=
{
(
s
i
,
s
i
+
1
)
∣
i
=
0
,
1
,
…
,
n
−
2
}
s
.
t
.
∀
s
i
,
s
j
f
(
s
i
)
≠
f
(
s
j
)
S = \{(s_i,s_{i+1}) \space | \space i = 0, 1, \dots , n-2\} \quad s.t. \space\forall s_i, s_j \quad f(s_i) \ne f(s_j)
S={(si,si+1) ∣ i=0,1,…,n−2}s.t. ∀si,sjf(si)=f(sj)
提取特征表示
S
∗
:
=
cnt
(
e
a
c
h
e
l
e
m
e
n
t
)
(
dim
(
S
∗
)
=
26
)
S
i
∗
≤
1
\boldsymbol{S^*} := \text{cnt}(each\space element)\quad(\dim(\boldsymbol{S^*})=26)\\ S^*_i \le1
S∗:=cnt(each element)(dim(S∗)=26)Si∗≤1
非重复子串要求子串中任意字母的个数不能大于1
总结
使用滑动窗口结合双指针,滑动窗口的表示采用哈希表存储字母数量信息的方式
在窗口滑动的过程中维护最大不重复子串长度