【算法】字符串中的最长无重复子串(三种解法)

问题: 求一个字符串中的最长无重复子串,或者说求一个数组中最长的无重复子数组(都是一种意思)

输入:abcdedafg

输出:5

解释:abcde这是一个最长的无重复子串,同理edafg也是。所以最长的无重复子串是5

也就是以下图中题目示意:
在这里插入图片描述

方法一:借助unordered_set和left,right指针

算法思想:left指针开始的时候指向set中的第一个元素。right向后遍历,遍历到的数字如果set中没有则加进myset中,同时全局变量统计出现无重复元素的最大值;如果right遍历到v[i]时,set中存在此数,则set中要从left指针的位置开始释放,直到释放掉次数的时候,再将v[i]加入进set中,继续往后遍历。 这样,全局变量就可以在过程中统计出现的无重复次数,所以O(N)的复杂度就可以完成。

算法实现

int main()
{
	int n = 0;
	int left = 0;
	int right = 0;
	int result = 0;
	cin >> n;
	vector<int> v(n);
	unordered_set<int> myset;
	for (int i = 0; i < n; i++)
	{
		cin >> v[i];
	}
	while (left < n && right < n)
	{
		if (!myset.count(v[right]))  // 如果myset中没有v[i],将它加入进来.
		{
			myset.insert(v[right++]); // 加入进来之后,right++,走向下一个位置
			result = max(result, right - left);
		}
		else
		{ // 如果myset中存在right值,则将myset中从第一个开始取出,直到把原来值与v[right]相等的取出,在把v[right]放进去
			myset.erase(v[left++]); 
		}
	}
	cout << result << endl;


	system("pause");
}

总结:字符串中最长无重复子串也可以这种方法实现,在这里类似,就不在实现了。

方法二:借助unordered_map巧妙完成统计

算法思想:与方法一不同的是,每一次的start都是直接取到哪个重复字符的位置。比如 abcdab, map中存放的是{{a,1},{b,2},{c,3},{d,4}},当a再次出现的时候,start可以快速定位到map中它的位置,是1。那现在的start就是1,算全局最大值的时候,就不算之前的a,算这个再次出现的a。所以全局最大值要 end-start+1。但是要注意将map中的值要换成第二次到来的下标,即{a,4}。因为后面字符串中还有a字符时,start还要更新位置。(不好理解的话,调试一波,方便理解)

算法实现

int main(){
	string A;
	while (cin >> A){
		int size = A.length();
		int res = 0;
		int start = 0;
		unordered_map<int, int> map;
		for (int end = 0; end < size; end++){
			if (map.count(A[end])){
				start = max(start, map[A[end]]);
			}
			map[A[end]] = end + 1;
			res = max(res, end - start + 1);
		}
		cout << res << endl;
	}
	return 0;
	
}

总结:总结两种方法虽然都是O(N),但是方法二明显要快于方法一,因为寻找start位置的时候直接就通过map找到。而方法一还要通过erase,增大开销。

方法三:借助滑动窗口

算法思想:类似之前的思路,使用 window 作为计数器记录窗口中的字符出现次数,然后先向右移动 right,当 window 中出现重复字符时,开始移动 left 缩小窗口,如此往复。显然还是O(N)

算法实现

int LengthOfLongestSubstring(string s)
{
	int left = 0;
	int right = 0;
	unordered_map<char, int> window; // 窗口
	int res = 0; //记录长度

	while (right < s.size())
	{
		char c1 = s[right];
		window[c1]++;
		right++;
		//如果window中出现重复字符
		//开始移动left缩小窗口
		while (window[c1] > 1)
		{
			char c2 = s[left];
			window[c2]--;
			left++;
		}
		res = max(res, right - left);
	}
	return res;
}

猜你喜欢

转载自blog.csdn.net/weixin_43939593/article/details/105578297