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

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

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

示例 1:

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

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

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

一、思路

(一)基本想法

一开始的基本想法是用一个循环遍历整个字符串:
设置一个哈希表用于查找当前字符是否重复。

  • 不重复,长度+1,继续
  • 重复,记录当前长度,并与之前的最大长度进行比较,记录下较长的
    随后重新开始计算子串长度

(1)出错1:
最后一次子串的长度没有参与比较导致结果出错

(2)出错2
这个想法有个漏洞:重新开始计算子串长度,在这一步中,由于事先没有记录下上一个子串的起始位置,因此新的子串永远是以重复位置作为起始点来计算长度的,所以会导致结果错误。

(二)改进方法

(1)设置两个循环:

  • 外层循环,确定子串的起始位置
    在某些条件下可以直接不进入内层循环直接结束
    能想到的条件是:当前最长子串的长度 = (字符串结束位置 - 当前子串起始位置)

  • 内层循环,确定子串的长度
    采用(一)的方法即可

(2)C++代码如下:

class Solution {
public:
	int lengthOfLongestSubstring(string s) {
		map<char, int> save_c;
		int len = 0, len_max = 0, j;

		for (j = 0; j < s.size(); j++) {
			if (len_max >= (s.size() - j))
				break;
			for (int i = j; i < s.size(); i++) {
				// 判断当前字符是否重复
				if (save_c.count(s[i]) != 0) {
					// 发生重复时,清空哈希表,并退出
					save_c.clear();
					if (len > len_max) {
						len_max = len;
					}
					len = 0;
					break;
				}
				save_c.insert(map<char, int>::value_type(s[i], 1));
				len++;
			}
			if (len > len_max)
				len_max = len;
		}
		return len_max;
	}
};

执行效率:
在这里插入图片描述
虽然勉强通过了,但效率上来说,被人吊打,嗯。。。果然还是想的太简单了。

(三)再次改进

(1)疑问
实际上,当前子串出现重复时,有必要将之前的哈希表进行清空吗?
不用,假设当前子串的起始位置是i,结束位置是j,可以知道的是第j个字符与第k个字符(k=i,i+1,…,j-1)重复
因此,起始位置在k之前且包含第k个字符的子串长度都不可能超过之前找到的子串的最大长度
所以,我们要做的就是找出这个k,从第k+1个字符作为起始位置,进行重复匹配

(2)C++代码


class Solution {
public:
	int lengthOfLongestSubstring(string s) {
		map<char, int> save_c;
		map<char, int>::iterator it;
		int len = 0, len_max = 0, j, k;

		for (j = 0; j < s.size(); j++) {
			if (len_max >= (s.size() - j))
				break;
			for (int i = j + len; i < s.size(); i++) {
				it = save_c.find(s[i]);
				// 判断当前字符是否重复
				if (it != save_c.end()) {
					// 在哈希表中找到了该字符,重复
					k = it->second;
					// 清除j--k的字符
					for (int m = j; m <= k; m++) {
						save_c.erase(s[m]);
					}
					// 判断当前子串长度是否大于之前找到的最大长度
					if (len > len_max) {
						len_max = len;
					}
					// 以第k+1个字符作为下一个子串的起始位置,再次进行匹配
					len = i - k;
					save_c.insert(map<char, int>::value_type(s[i], i));
					j = k;
					break;
				}
				save_c.insert(map<char, int>::value_type(s[i], i));
				len++;
			}
			if (len > len_max)
				len_max = len;
		}
		return len_max;
	}
};

执行效率:
在这里插入图片描述
可以看出比之前快上了十几倍,但是也仅仅击败了60%的人,说明算法还有有待改进
更进一步的改进方法还没有想出了,凭直觉,如果想要更快一步,应该需要换一个思路了,这个就留到第二轮刷题再说了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值