题目描述
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
数据范围:s.length≤40000
方法一:滑动窗口+哈希表
思路:
- 使用滑动窗口,保证窗口内部元素都是不重复的,然后窗口右边界不断向右滑,如果窗口内出现重复字符,说明刚加入的元素与窗口内某元素相同了,只需收缩窗口左边界到重复元素的下一个字符处,就可使得窗口内元素重新保持不重复。
- 使用哈希表来保证窗口内的元素不重复,key值为窗口内的元素,value值为其出现的次数,只要新加入窗口的元素出现次数不为1,即表示重复。
当新加入元素重复,窗口左边界右移,同时在哈希表中要其经过的元素频率减1,保证哈希表中的频率都是窗口内元素的频率。
// 直到left移动到重复的元素s[right]处,并将其频率减一,left++到其下一个元素,循环结束
while(mp[s[right] > 1])
mp[s[left++]]--;
代码实现
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> mp; //哈希表记录窗口内非重复的字符
int res = 0;
for(int left = 0, right = 0; right < s.length(); right++){ //left:左边界;right:右边界
mp[s[right]]++; //窗口右移进入哈希表统计出现次数
while(mp[s[right]] > 1) //出现次数大于1,则窗口内有重复
mp[s[left++]]--; //窗口左移,同时减去该字符的出现次数
res = max(res, right - left + 1); //维护子串长度最大值
}
return res;
}
方法二:动态规划+哈希表
思路:
- 如果对于某个前面的字串,新加入一个字符,与之前的都不重复,那么最长子串就是在之前的基础上长度+1;如果与之前的重复了,那么长度就是当前位置减去它重复之前字符出现的位置。
- 哈希表的key值记录出现过的字符,value值记录其出现的位置。
具体做法:
- dp[i]表示以下标i为结尾的字符串最长不含重复子串的长度。
- 遍历字符串,若哈希表没有出现过即不重复,dp[i] = dp[i-1] + 1。
- 若哈希表出现过,则当前元素重复,考虑dp[i] = i - mp[s[i]]。但由于每次跟新重复字符后,其之前出现的字符依然在哈希表中用于判断是否重复,所以实际仍需考虑其前一个字符的基础,即dp[i] = min(dp[i-1] + 1, i - mp[s[i]]).
- 之前重复的元素之前的字符(如果后面没出过即为更新过)其i - mp[s[i]] 一定大于 dp[i-1] + 1,对于更新后的子串其实不算做重复字符,dp[i]取的为dp[i-1] + 1;
- 而在上次重复元素后更新过的字符(即出现在更新后子串中的),其i - mp[s[i]] 一定小于 dp[i-1] + 1, 那么子串重复,更新子串,dp[i]取 i - mp[s[i]];
假设:dp[i] = i - mp[s[i]]
a b c c d a mp a b c
dp 1 2 3 0 1 2
i 0 1 2 3 4 5
dp[3] = 3 - 2 = 1
mp[c] = 3 //更新mp
a b c c d a mp a b c d
dp 1 2 3 1 2 0 1 3 4
i 0 1 2 3 4 5
dp[5] = 5 - 0 = 5
mp[a] = 5
a b c c d a mp a b c d
dp 1 2 3 1 2 5 5 1 3 4
i 0 1 2 3 4 5
然而最长不重复子串为 c d a 长度为3,明显与事实不符。
而考虑其前一个的基础,即dp[i] = min(dp[i-1] + 1, i - mp[s[i]])
dp[5] = min(2 + 1, 5 - 0) = 3 符合结果。
实现代码:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int> mp;
int res = 1; //子串长度最短为1
vector<int> dp(s.length(),0); //dp[i]表示以下标i结尾的字符串最长不含重复子串的长度
dp[0] = 1; //第一个字符作为子串长度为1
mp[s[0]] = 0; //位置为0
for(int i = 1; i < s.length(); ++i){
if(mp.find(s[i]) == mp.end())
dp[i] = dp[i-1] + 1;
else
dp[i] = min(dp[i-1] + 1,i - mp[s[i]]);
mp[s[i]] = i; //更新mp
res = max(res,dp[i]);
}
return res;
}