leetcode 3. 无重复字符的最长子串----滑动窗口篇1,双指针篇1
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode 3. 无重复字符的最长子串----滑动窗口篇1,双指针篇1相关的知识,希望对你有一定的参考价值。
无重复字符的最长子串题解集合
滑动窗口—双指针法
思路:
这道题主要用到思路是:滑动窗口
什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!
一直维持这样的队列,找出队列出现最长的长度时候,求出解!
时间复杂度:O(n)
图解
代码:
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
if (s.empty()) return 0;
int start(0), end(0), maxLength(1),len(0);
for (; end < s.size(); end++)
{
char temp = s[end];
for (int j = start; j < end; j++)
{
if (s[j] == temp)//当前滑动窗口出现重复字符
{
start = j + 1;
//计算start指针更新后,当前滑动窗口内元素的个数,注意此时end指针指向的元素已经和滑动窗口里面任何一个元素都不重合
//一会还需要把end指针指向的元素放入更新后的滑动窗口内,就len的长度一会还需要加一
len = end - start;
break;
}
}
//如果当前end指针指向的元素与滑动窗口内的某个元素重复,那么start指针需要更新到新的位置,当start指针更新到新的位置后
//我们还需要把原先没有纳入滑动窗口范围,即end指针指向的元素纳入滑动窗口范围,即len的长度要加一
len++;
maxLength = max(maxLength, len);
//然后end指针再继续后移
}
return maxLength;
}
};
哈希表优化
注意: 哈希容器存储的是把当前字符作为关键字,将当前字符的下标作为值,当出现重复的字符时,就把start指针移动到前面一个重复字符位置之后,然后将后一个新出现的重复字符下标覆盖原来的字符下标
代码:
class Solution {
unordered_map<char, int> cache;
public:
int lengthOfLongestSubstring(string s)
{
if (s.empty()) return 0;
int start(0), end(0), maxLength(1),len(0);
for (; end < s.size(); end++)
{
char temp = s[end];
//出现重复字符----注意:仅当s[start,end) 中存在s[end]时更新start
//因为随着滑动窗口大小和位置的变化,可能某些数据原先已经存入了哈希表中,但是随着滑动窗口大小和位置的变化
//这些数据离开了滑动窗口的范围,但是数据依然保留在哈希表内部
//因此这里要限制必须当前元素是与滑动窗口里面的元素重复,不包括外部
if (cache.find(temp) != cache.end()&&cache[temp] >= start)
{
start = cache[temp] + 1;
len = end - start;
}
cache[temp] = end;
len++;
maxLength = max(maxLength, len);
}
return maxLength;
}
};
数组(桶)代替哈希表
注意:这里选择ascall码做桶,数组大小为128.因为ascall的范围是0----127,数组内所有元素初始值为-1,当某个字符出现时,就将其的ascall码对应在数组中的位置值改成当前字符下标
如何判断是否出现重复元素呢?
如果利用当前字符ascall对应去数组找位置,发现该位置的值不为-1,说明之前该元素已经出现过了,需要更新其对应的下标
当然这里还有一个技巧,如果原本在滑动窗口内的元素,后来因为滑动窗口右移而脱离滑动窗口了,那么它对于的下标一定比start下标小,因为start是滑动窗口最左段的下标,可以利用此判断当前元素是与滑动窗口内元素重复还是外部
这里其实类似哈希映射,也同样要注意当前字符是与滑动窗口范围内的元素进行比较,判断是否与某个字符重复,而不是与滑动窗口外的字符进行比较
代码:
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
if (s.empty()) return 0;
int start(0), end(0), maxLength(1),len(0);
vector<int> ascall(128, -1);
for (; end < s.size(); end++)
{
char temp = s[end];
//说明当前元素是和滑动窗口内元素产生重复
if (ascall[temp] >= start)
{
start = ascall[temp] + 1;
len = end - start;
}
//更新当前产生重复元素的下标
ascall[temp] = end;
len++;
maxLength = max(maxLength, len);
}
return maxLength;
}
};
动态规划
1、“状态”的定义
dp[i]:从字符串s起始位置开始到i位置的最长不重复子串长度
2、推导“状态转移方程”
分两种情况:
1)第i个字符不参与到当前字符串的最长子串,有dp[i]=dp[i-1]。
2)第i个字符参与到当前字符串的最长子串,检查从第i个字符往前数dp[i-1]个字符长度的子串,若没有重复,则有dp[i]=dp[i-1]+1,若有重复,则为情况1)。
最终当前dp[i]的结果,取决于上面两种选择中结果较大者,那么显然第i个字符参与进来后得到的长度肯定比不参与得到的大.
但是能否参与进来,取决于第i个字符有没有与前面dp[i-1]个字符产生重复,如果重复了那么就只能维持dp[i-1]的结果
这里选择了第i个字符后,需要去查看当前字符i和前面dp[i-1]个字符组成的字符串中是否存在两两甚至更多重复的元素
这里可以截取出从i位置往前i-1个字符组成的字符串,然后对字符串进行排序,方便两两比较,查看是否有重复字符
代码:
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
if (s.empty()) return 0;
vector<int> dp(s.size());
//当i=0时,即只有一个字符的时候,那么最长不重复子串长度就是1
dp[0] = 1;
for (int i = 1; i < dp.size(); i++)
{
//判断当前字符是否与前面dp[i-1]个字符中的字符重复
bool found = false;
//这里需要判断从位置i起往前dp[i-1]个字符里面是否存在两两或者多个重复元素
//截取需要判断是否有重复元素的子串,并且进行排序,方便两两比对判断是否有重复
string currentString = s.substr(i - dp[i-1],dp[i-1] + 1);
sort(currentString.begin(), currentString.end());
for (int j =0; j <dp[i - 1]; j++)
{
if (currentString[j] == currentString[j + 1])
{
found = true;
break;
}
}
//产生了重复,说明要维持当前最长不重复子串长度
if (found)
dp[i] = dp[i - 1];
else
dp[i] = dp[i - 1] + 1;
}
return dp[dp.size() - 1];
}
};
以上是关于leetcode 3. 无重复字符的最长子串----滑动窗口篇1,双指针篇1的主要内容,如果未能解决你的问题,请参考以下文章