数据结构与算法---字符串---无重复字符的最长子串

博客围绕无重复字符的最长子串问题展开,给出暴力法和DP两种思路。暴力法通过双指针遍历,需解决判断重复字符问题,可根据字符范围用数组或hashMap;DP思路先判断为最值问题且满足无后效性,给出解题步骤及转移方程,同样涉及判断重复字符。

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

无重复字符的最长子串

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

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

示例 1:

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

示例 2:

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

示例 3:

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


思路1:暴力法

pwwkew为例:

设置leftIndex = 0,rightIndex = 0;
求p,最长为1
pw,最长为2
pww,存在重复字符串,因此放弃,leftIndex++;

w,最长为1
ww,有重复,放弃,leftIndex++;

w,最长为1
wk,最长为2
wke,最长为3
wkew,存在重复字符串,因此放弃,leftIndex++;

k,最长为1
ke,最长为2
kew,最长为3,由于rightIndex = nums.length - 1;leftIndex++;

e,最长为1
ew,最长为2,由于rightIndex = nums.length - 1;leftIndex++;

k,最长为1

需要解决的问题是,黄色部分,如何判断存在重复字符串?
假设,字符只包括26个字母,或者128个ASCII码,我们可以建立一个数组array,数组大小为26或者128,将每一个遍历到的字符存进数组,在判断新的字符的时候,查找数组对应的值array[i]是否>0
如果array[i]>0,说明已经存在对应的值,存在重复字符串
如果array[i] ==0,说明,不存在重复的字符

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s == null) return 0;
        char[] charArray = s.toCharArray();
        if(charArray.length == 0) return 0;

        
        int max = 1;
        for(int leftIndex = 0; leftIndex < charArray.length; leftIndex++)
        {
            //128个数组元素,preIndexArray[i] = j;表示第i个字符里面有j个
            char[] preIndexArray = new char[128];

            preIndexArray[charArray[leftIndex]]++;
            int length = 1;//字符长度

            for(int currentIndex = leftIndex + 1; currentIndex < charArray.length; currentIndex++){
                if(preIndexArray[charArray[currentIndex]] > 0){
                    
                    break;
                }else{
                    preIndexArray[charArray[currentIndex]]++;
                    length++;
                }
            }
            max = Math.max(max, length);
        }
        return max;
    }
}

时间复杂度为:O(n^2)
空间复杂度为:O(n)

假设,字符不限于26个字母,也不限于128个ASCII码,也就是字符char什么都可以存,那么,我们就不能使用数组了,只能使用hashMap

思路2:DP

最值问题,考虑是否可以使用DP

是否为最值问题?

求无重复字符的最长子串,因此,是最值问题

能否使用DP?
  1. 原问题拆分为子问题
    在求"pwwkew"的无重复字符的最长子串,可否变为求"pwwke"的无重复字符的最长子串,求完之后,结合前面的结果推算包含w的无重复字符的最长子串

在这里插入图片描述
2. 无后效性
前面的结果确定后,不会再改变,满足无后效性

DP解题步骤?
  1. 确定含义
    dp(nums[i]),以nums[i]结尾的无重复字符的最长子串
  2. 边界值
    dp(nums[0]) = 1;
  3. 转移方程
    如果dp(nums[i - 1])里面,没有nums[i]一样的字符,则dp(nums[i]) = dp(nums[i - 1]) + 1;
    如果dp(nums[i - 1])里面,存在nums[i]一样的字符,假设该字符存在的位置是pi,则dp(nums[i]) = i - pi + 1;

在这里插入图片描述

注:
图中s[i],跟上面提到的nums[i]是一个东西
图中li,指的是nums[i - 1]的左边界
图中pi,指的是nums[i]字符,上一次出现的地方。这个作用主要体现在,不需要一步一步的变量查找


如何判断存在重复字符串?

假设字符只包括28个字母或者128个ASCII码,使用数组

之前在暴力法里面,array[i] = j;的含义是:字符i,有j个
现在,我们使用array[i] = j;表示的含义是:字符i,存储的位置为j

假设不限于28个字母,也不限于128个ASCII码,使用哈希map

先来个简单点的,假设,字符只包含128个ASCII码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        //第一步:判断为空的边界值
        if(s == null) return 0;

        //字符串转换为数组
        char[] sArray = s.toCharArray();
        if(sArray.length == 0) return 0;
        //至少有一个数据
        
        //preIndexArray[i] = 3;符号i存在的位置为3
        int[] preIndexArray = new int[128];
        for(int i = 0; i<preIndexArray.length; i++){
            preIndexArray[i] = -1;
        }
        preIndexArray[sArray[0]] = 0;

        int max = 1;
        //dp[i - 1]的左边边界
        int leftIndex = 0;

        for(int i = 1; i < sArray.length; i++){
            int preIndex = preIndexArray[sArray[i]];
            if(preIndex >= leftIndex){//之前出现的地方,在上一个左边界的右边或相等
                leftIndex = preIndex + 1;
            }
            preIndexArray[sArray[i]] = i;
            max = Math.max(max, i - leftIndex + 1);
        }
        
        return max;
    }
}

现在,来看一下字符不限于ASCII码128个的时候

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s == null) return 0;
        char[] charArray = s.toCharArray();
        if(charArray.length == 0) return 0;

        int max = 1;
        int leftIndex = 0;
        //建立hashMap,k是字符charArray[i],value是字符存在的位置
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        map.put(charArray[0], 0);
        for(int i = 1; i<charArray.length; i++){
            Integer preIndex = map.get(charArray[i]);
            if(preIndex != null && preIndex >= leftIndex){
                leftIndex = preIndex + 1;
            }
            //存储preIndex到哈希map中
            map.put(charArray[i], i);
            max = Math.max(i - leftIndex + 1, max);
        }
        return max;
    }
}

需要注意的地方:
map的创建以及取值、存储操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值