无重复字符串的最长字串问题分析

本文探讨了解决字符串中找到最长无重复字符子串的问题,从初始的暴力遍历方法改进到使用滑动窗口和哈希表,优化了时间复杂度。详细介绍了几种解决方案,并重点讲解了哈希表在字符判断上的高效应用。

题目如下

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度

示例 1:

输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

提示:

0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成

思路分析

这题我的最开始思路是:

用一次for循环遍历,用一个字符串储存结构,字符串初始为空,遇到不重复的字符就把它加入字符串,遇到重复的字符就把字符串清空,统计下一个字符串,这样记录下一个最长的字符串,就是题目的解。

但是,我发现了一个问题,用这种方法得到的结果并不完善,处理一些数据时会出错。比如dvdf,用这种方法得到的结果是2,但是很明显,结果应该是3,vdf。

很显然,出现这种状况的原因是没有有效的把每一个元素当成字符串首元素考虑造成的,程序执行时直接没有管第二个元素,跳过去了。

于是,我想到在最外层加一个循环,遍历长度为n的字符串每一个字符,都当作首地址一一处理,其他部分逻辑和之前一样。

果不其然,结果是对的,但是看了看运行时间,普遍都在400ms左右,难道没有有更好的方法?

我参考了官方的思路,我们使用的方法就像是一个滑动的区间,区间头也就是字符串的首地址,那么我们程序得到的每一个字符串,其实内部都是不同的字符。那么在判断相同字符这一步,下一个字符串就没有必要一个一个的重新判断修改了,只要删掉前一个字符串的首位,就可以直接跳到字符串尾部的下一位进行重复判断。

当然程序还有一些需要注意的点,比如最长字符的记录,我是用了一个if来处理一些特殊情况,比如“bbbb”这类最大长度小于1的字符串。

这样一来,判断过程中的时间复杂度就会大大减小,修改后发现运行时间只有原来的20分之1.

但是这还不是最优解法,在判断上我们还可以继续优化时间复杂度。

那就是使用哈希表,哈希表的好处在于可以快速判断字符是否已经出现,从而避免大量的查找,c++中stl中可以直接使用,但是如果你不会,其实也可以自己建一个简单的哈希表。

题目中已经告诉我们,字符串包括空格和字母和空格、符号。那么根据ascll码,我们就可以直接建一个哈希表b[130].一旦字符出现,就将对应acsll码b[i]置为1,否则为0;

这样就可以可以快速判断。

以下是解决方案的函数(哈希表未使用):

int lengthOfLongestSubstring(string s) {
    string a;
	static int n,l=0,ram;
	bool b[130];
    ram=0;
    n = s.length();
    l=0;

	for (int d = 0; d < n; d++)
	{
		for (int i = ram; i < n; i++)
		{
				if (b[(int)s[i]] ==true)
				{
					if (a.size() > l)
					{
						l = a.size();
					}
                    ram=i;
					a.erase(a.begin());
					break;
				}
			a.append(s, i, 1);
			b[(int)s[i]]=true;
		}
	}

	if (l <a.size() && s.size() != 0)
	{
		l = a.size();
	}
	return l;
    }

使用自制简单哈希表的代码,平均用时更短:

int lengthOfLongestSubstring(string s) 
	{
		string a;
		static int l = 0, ram;
		bool b[130] = { 0 };
		ram = 0;
		l = 0;

		for (int d = 0; d < s.length(); d++)
		{
			for (int i = ram; i < s.length(); i++)
			{
				if (b[(unsigned int)s[i]] == true)
				{
					if (a.size() > l)
					{
						l = a.size();
					}
					ram = i;
					b[(unsigned int)a[0]] = false;
					a.erase(a.begin());
					break;
				}
				a.append(s, i, 1);
				b[(unsigned int)s[i]] = true;
			}
		}

		if (l < a.size() && s.size() != 0)
		{
			l = a.size();
		}
		return l;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值