请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路1:利用滑动窗口,这个窗口中对应的字符是没有重复的。首先我们需要两个指针来指示左右边界。start
表示左边界,end
表示右边界,用一个哈希表存储我们遍历过的字符的,以此来指示是否遍历到了一个重复的字符。
注意我们遍历的时候,有可能这个重复字符会出现在当前左边界start
的左边,那么我们没必要把start
往回退,因为这肯定不符合题意的。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int> table;
int ans = 0;
for(int end = 0,start = 0;end<s.size();++end)
{
char alpha = s[end];//判断当前字符是什么
if(table.find(alpha) != table.end())//表示有重复
start = max(table[alpha],start);//保证不回溯
ans = max(ans,end - start + 1);//ans已经保留了以前找到的最长子串的值
table[alpha] = end + 1;//否则就将当前字符的下一位置加入
}
return ans;
}
};
思路二:动态规划
我们用f[i]
来表示到索引为i
这个位置的最长不重复子串的长度。
初始条件:f[0] = 1
,即到索引为0
这个字符的最长不重复子串的长度为1
。
我们需要用一个哈希表来存储遍历过的不重复字符的索引下标。
那么转移方程:
若当前字符并未出现过则:f[i] = f[i -1] + 1
。
若当前字符已经出现过,并且和上次出现这个字符的位置之差小于等于f[i - 1]
,例如字符串arabcacfr
,字符a
距离上次出现的距离d
为2
,而f[1] = 2
,那么到这个a
的最长不重复子串就是d
,即f[i] = d
若当前字符已经出现过,并且和上次出现这个字符的位置之差大于f[i - 1]
,例如字符串arabcacfr
,字符r
距离上次出现的距离d
为7
,而f[7] = 3
,那么到这个r
的最长不重复子串,仍然是f[i] = f[i - 1] +1
,因为我们不能回溯超过f[i - 1]
长度的位置,毕竟位置之差大于f[i - 1]
已经证明重复的字符一定在前面的子串中,而不是发生在已经得到的最长不含重复字符的子串中。所以此时的f[i] = f[i - 1] + 1
代码:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n = s.size();
if(n == 0)
return 0;
unordered_map<char,int> table;
vector<int>f(n,0);
f[0] = 1;
table[s[0]] = 0;
for(int i = 1;i < n;++i)
{
char alpha = s[i];//判断当前字符是什么
if(table.find(alpha) != table.end())//表示有重复
{
int d = i - table[alpha];
if(d <= f[i - 1])
{
f[i] = d;
}
else
{
f[i] = f[i - 1] + 1;
}
table[alpha] = i;//这里要更新重复字符比较新的位置
}
else
{
f[i] = f[i - 1] + 1;
table[alpha] = i;
}
}
return *max_element(f.begin(),f.end());
}
};