3——无重复字符的最长子串(Longest Substring Without Repeating Characters)

本文探讨了两种求解最长无重复子串长度的方法:使用Map的第一次解法和利用滑动窗口与HashSet的第二次解法。通过实例解析,详细说明了每种方法的实现过程与优劣对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

示例1

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

示例2

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

示例3

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

第一次解法(Map):

我使用了Java中的Map,每次检测是否含有当前字符,若无则将当前字符添加进maps;若有,将当前字符串长度 cur_num +1。若当前长度大于最大长度,则记录最大长度。若最大长度大于剩余字符个数,直接退出循环。

向 HashMap 中添加元素后,元素的顺序会被改变,而 LinkedHashMap 则是按顺序放置的。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> maps = new LinkedHashMap<Character, Integer>();
		int max_num = 0, cur_num = 0;
		int count = s.length();
		if (count != 0) {
			for (int i = 0; i < s.length(); i++) {//控制循环次数
				maps.clear();
				cur_num = 0;
				int pos = 0;
				for (int j = i; j < s.length(); j++) {
					if (maps.containsKey(s.charAt(j))) {
						break;
					} else {
						maps.put(s.charAt(j), pos);
						pos++;
						cur_num++;
					}
				}
				if (cur_num > max_num) {
					max_num = cur_num;
				}
				if (max_num >= (s.length()-i-1)) {
					break;
				}
			}
		}
        return max_num;
    }
}

第二次解法(滑动窗口)

Java集合类Set

Set 是一种无序、可重复的数据容器,与 List 一样继承于 Collection 接口,HashSet 和 TreeSet,这两种 Set 是日常工作中用的比较多的。

HashSet 和 TreeSet 增删改查操作一样
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

public class Test {

	public static void main(String[] args) {
		//System.out.println();
		String a_str = "Java", b_str = "C++", c_str = "Python", d_str = "C";
		Set<String> set = new HashSet<>();
		set.add(a_str);	//增
		set.add(b_str);
		set.add(c_str);
		set.add(d_str);
		for (String value : set) {	//查
			System.out.print(value + " ");
		}
		System.out.println();
		set.remove(a_str);	//删
				//改:	java的HashSet文档貌似没有提供直接的修改操作。间接的做法是先删后增
		for (String value : set) {	//查
			System.out.print(value + " ");
		}
	}
}

Set常见方法
Sr.No.Method & Description
1

add( )         向集合中添加元素

2

clear( )        去掉集合中所有的元素

3

contains( )    判断集合中是否包含某一个元素

4

isEmpty( )    判断集合是否为空

5

iterator( )    主要用于递归集合,返回一个Iterator()对象

6

remove( )    从集合中去掉特定的对象

7

size( )        返回集合的大小





本题实现代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        Set<Character> set = new HashSet<>();
		int max_num = 0;
        int i = 0, j = 0;
		int len = s.length();
        while (i < len && j < len) {
            if (! set.contains(s.charAt(j))) {
                set.add(s.charAt(j++));
                max_num = Math.max(max_num, j-i);
            } else {
                if (max_num >= (len-i-1)) {
			        break;
                }
                set.remove(s.charAt(i++));   
            }
        }
        return max_num;
    }
}





我所理解的滑动窗口:比如一个字符串" pwwkew ",将字符一个一个放进窗口(相当于一个队列)中。

  1. p —— max_num = 1
  2. p,w —— max_num = 2
  3. p,w,w —— max_num = 2(这时候已有一个w在队列中,这是需要将前面个w及其之前的所有元素移出队列),变为 w —— max_num = 2
  4. w,k —— max_num = 2
  5. w,k ,e —— max_num = 3
  6. w,k ,e,w —— max_num = 3(这时将前面个w移出队列),变为 k ,e —— max_num = 3
    (而在第五步时,i = 2, s.length() = 6, 此时 max_num = 3, max_num 大于等于后续未检测到的剩余字符总数(s.length() - i - 1),那么就不用继续下去了,使用 break)。




使用 HashSet 而非 ArrayList 的原因是 ArrayList 的 contains 方法的效率不高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值