LeetCode--3.无重复字符的最长子串

题目描述

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

示例 :

输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3

解题思路:

根据给出的示例,首先将字符串通过toCharArray()转化为字符数组,然后遍历数组元素并将其插入的List集合中,同时判断集合中是否已经存在该字符。如果存在,则计算插入元素前的集合长度,然后将集合清空,将当前字符插入空白集合中,否则继续插入新的数组元素。最终最长的集合长度即为最长子串的长度。代码如下:

private static int lengthOfLongestSubstring(String s) {
    // 创建一个int变量用于存储最长字符串的长度
	int maxLength = 0;
    // 创建一个list对象用于存储字符
	List<Character> subString = new ArrayList<>();
	// 将字符串转换为字符数组
	char[] chs = s.toCharArray();
	int length = 0;
    // 遍历字符数组
	for (char c : chs) {
        // 判断当前集合是否包含当前字符
		if (!subString.contains(c)) {
            // 如果不包含则向集合中添加该字符
			subString.add(c);
		}else {
            //否则,计算当前集合的长度,并清空集合后将该字符插入集合
			length = subString.size();
			subString.clear();
			subString.add(c);
		}
        // 如果集合中只存在一个元素或者重复元素,则不会进入else语句,所以此处需要添加一个判断
		length = (length >= subString.size()) ? length : subString.size();
        // 每次循环完成后,将得到的子字符串长度和之前的maxLength作比较,如果大于则替换maxLength,反之则保持不变
		maxLength = (length >= maxLength) ? length : maxLength;
	}
    // 返回最大子字符串长度
	return maxLength;
}

运行以上代码,能通过测试用例,但提交代码却出现bug。经检查,发现代码中的subString.clear()有问题,这里不应该将subString清空,如字符串abcdbfg,如果采用上面的代码,当读取到第二个b时,调用clear()就会将集合中的abcd给删掉,最后的结果就是bfg,而正确的结果是cdbfg。正确的方式是在插入b之前,将最后一个b之前的元素给删掉。代码如下:

private static int lengthOfLongestSubstring(String s) {
	int maxLength = 0;
	List<Character> subString = new ArrayList<>();
	
	char[] chs = s.toCharArray();
	int length = 0;
	for (char c : chs) {
		if (!subString.contains(c)) {
			subString.add(c);
		}else {
			length = subString.size();
            // 获取插入元素c之前,最后一个c的下标
			int index = subString.lastIndexOf(c) + 1;
            // 将最后一个c之前的元素(包括c)删除,取subString的子集合
            subString = subString(index,length);
			subString.add(c);
		}
		length = length >= subString.size() ? length : subString.size();
		maxLength = (length >= maxLength) ? length : maxLength;
	}
	return maxLength;
}

以上代码经测试,能通过abcdbfg示例,但是提交后仍然出错,987个测试用例通过了986个,最后一个提示超出时间限制。测试上述代码,发现时间主要花在以下两行代码上。

// 使用这行代码比subString.clear()平均多花10ms左右
int index = subString.lastIndexOf(c) + 1;
// 而该行代码直接将运行时间从60ms拉长至200+,最终导致时间超长
subString = subString.subList(index, length);

经考虑,上面的代码是将重复字符前面的元素从subString中删除从而取子集合,可以换一种思路,不用删除元素,而是将最后一次出现的重复元素的下标记录下来,然后和集合的length相减,同样相当于得到子集合的长度。具体代码如下:

private static int lengthOfLongestSubstring(String s) {
	int maxLength = 0;
	List<Character> subString = new ArrayList<>();
	
	char[] chs = s.toCharArray();
	int index = 0, length = 0 ,lastIndex = 0;
	for (char c : chs) {
		if (!subString.contains(c)) {
			subString.add(c);
		}else {
            // 记录元素c最后一次出现的下标,该下标只能往前不能往后
			index = index > (lastIndex = subString.lastIndexOf(c)+1) ? index:lastIndex;
			subString.add(c);
		}
        // 相当于计算子集合的长度
		length = subString.size() - index;
		maxLength = length >= maxLength ? length: maxLength;
	}
	return maxLength;
}

提交代码,可通过所有的测试用例,时间也控制在30ms左右,后续在思考更优的实现方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值