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的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 无重复字符的最长子串

[LeetCode]无重复字符的最长子串

leetcode题解#3:无重复字符的最长子串

Python版[leetcode]3. 无重复字符的最长子串(难度中等)

LeetCode3. 无重复字符的最长子串

力扣Leetcode 3. 无重复字符的最长子串