Leetcode: 不含重复字符串的最长子串长度

本文介绍了一种求解字符串中最长无重复字符子串长度的高效算法,提供了两种实现方法:一种使用HashSet,另一种利用HashMap进行优化。通过具体示例展示了如何运用这两种方法解决问题。

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

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

示例 :

输入: "abcabcbb"

输出: 3 

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

流程:

1 移动窗口方式的解法(set集合方式)

2 移动窗口的优化解法(hashMap方式)

 

动图(移动窗口方式的解法)借用五分钟学漫画动图

 

移动窗口方式的解法(HashSet)

1 通过一个集合来保存不重复的字符串

2 设置左边指针,用来表示移动窗口的范围

3 如果当前窗口遍历的字符出现过,则缩小窗口边界,即移除集合中的元素,继续遍历,如果没有出现过,则加入集合,并且计算最大连续数

4 直到左指针或者指针无法移动则停止

/**
 * 题目 :给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
 * 例如:
 *        输入: "abcabcbb"
 *        输出: 3
 *
 * @author pengjie_yao
 * @date 2019/7/17 11:38
 */
public class UniquelongString {

    public static void main(String[] args) {

        // 第一种HashSet解法
//        int result = uniqueLongString("ABCBBCDAB");
        // 第二种 HashMap解法
        int result = uniqueLongString1("ABCBBCDAB");
        System.out.println(result);
    }

    /**
     *  使用HashSet的解法
     *   该方法的时间复杂度: O(2n) = O(n)
     *   空间复杂度为:O(min(m,n))滑动窗口法需要 O(k)O(k) 的空间,
     *                其中 k 表示 Set 的大小。而 Set 的大小取决于字符串 nn 的大小以及字符集 / 字母 mm 的大小。
     * @param chars
     * @return
     */
    public static int uniqueLongString(String chars) {
        int length = chars.length();
        // 用来保存无重复的最长字符串
        Set<Character> hashSets = new HashSet<>();
        int max = 0;
        // 左指针
        int i = 0;
        // 右指针
        int j = 0;
        while (i < length && j < length) {
            // 1 如果set中有该元素,说明有重复出现,则将左指针向右移动
            if (hashSets.contains(chars.charAt(j))) {
                // 已有字符串
                hashSets.remove(chars.charAt(i));
                i++;
            }else {
                // 2 如果set中没有该元素,说明是新元素,则将右指针向右移动
                hashSets.add(chars.charAt(j));
                j++;
                // 最大连续数则取左右指针的差值与当前最大连续数之间的最大值
                 max = Math.max(max, j - i);
            }
        }
        return max;
    }

    
}

使用HashMap来优化移动窗口

使用HashMap,key作为保留的字符串,val作为数组的索引值,则每次遍历数组的时候,我们可以直接判断如果当前字符串在map中,则获取map中对应的索引值,直接让左指针从索引值处开始。

第一步 遍历第一个元素,则map中没有,放入集合中,右指针向右移动,记录max为1

第二步 遍历第二个元素,map中没有,继续放入,且此时max=2

第三步 遍历继续,此时max = 3

第四步,当遍历到元素b的时候,则我们则我们将左指针直接以b出现的索引值位置(即直接把移动窗口的左边界设置为出现重复数字的下一个位置),并更新map中的元素位置,此时最大值则是原最大值与左右指针差值之间的最大值作为做最大值,即max = MAX(3,(right-left))。

注意: 这里为什么要把left更新为出现b的位置,还记得第一种解法思路吗,用set集合保存数据,当发现有重复元素的时候,移除set中的重复元素。而hashmap在这里则直接更新掉元素的下标作为移除元素的替换,一样的想法不一样的实现罢了。

第五步,遍历到a,这时a是集合中有的,则获取到的索引位置为0,此时我们不能直接让left=0,这样的话,则算出来最大值为4,这样不符合实际情况,我们获取的数值要和left当前数值比较后取最大才可以才是正确的。

第六步 遍历到d,则加入集合,计算最大值

代码:

    /**
     *  使用HashMap的解法:
     *  例如:  s[j] 在 [i, j)[i,j) 范围内有与 j'重复的字符,我们不需要逐渐增加i。
     *         我们可以直接跳过 [i,j'] 范围内的所有元素,并将 i 变为 j' + 1
     *
     * @param chars
     * @return
     */
    public static int uniqueLongString1(String chars){


        int length = chars.length();


        Map<Character, Integer> map = new HashMap<>();
        // 左区域指针
        int i=0;
        // 右区域指针
        int j=0;
        int max = 0;
        for (j=0; j < length; j++) {
            if (map.containsKey(chars.charAt(j))) {
                i = Math.max(map.get(chars.charAt(j)), i);
            }
            map.put(chars.charAt(j), j);
            max = Math.max(max, j - i );
        }
        return max;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值