最长不含重复字符的子字符串

剑指offer的一道经典题目,难度中等。

描述

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

数据范围:

 s.length≤40000

示例1

输入:

"abcabcbb"

返回值:

3

说明:

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

示例2

输入:

"bbbbb"

返回值:

1

说明:

因为无重复字符的最长子串是"b",所以其长度为 1。    

示例3

输入:

"pwwkew"

返回值:

3

说明:

因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是子串的长度,"pwke" 是一个子序列,不是子串。    
方法:动态规划+哈希表

知识点:

动态规划算法的基本思想是:将待求解的问题分解成若干个相互联系的子问题,先求解子问题,然后从这些子问题的解得到原问题的解;对于重复出现的子问题,只在第一次遇到的时候对它进行求解,并把答案保存起来,让以后再次遇到时直接引用答案,不必重新求解。动态规划算法将问题的解决方案视为一系列决策的结果。

思路:

如果对于某个前面的子串,如果我们新加入一个字符,与前面的都不重复,那么最长无重复子串肯定就是在前面的基础上加1,如果与前面重复了,那就是当前位置减去它重复之前字符出现的位置的长度。因此我们使用动态规划递推。

具体做法:

  • 第一步:dp[i]表示以下标i结尾的字符串最长不含重复子串的长度,用哈希表记录是否重复出现字符,并记录其位置。
  • 第二步:遍历字符串,哈希表中没有出现过的就不是重复,因此考虑dp[i]=dp[i−1]+1,即在前一个字符的基础上加上它。
  • 第三步:哈希表中出现过的,这是重复的字符,考虑i−mp[s[i−1]],但是为了防止中间出现其他重复的字符,还是应该考虑它的前一个字符的基础,因此实际转移方程为dp[i]=min(dp[i−1]+1,i−mp[s[i−1]])。
  • 第四步:遍历过程中遇到的字符都要加入哈希表,同时维护最大的长度。

Java实现代码:

import java.util.*;
public class Solution {
    public int lengthOfLongestSubstring (String s) {
        //哈希表记录窗口内非重复的字符及其下标
        HashMap<Character, Integer> mp = new HashMap<>(); 
        int res = 0;
        //dp[i]表示以下标i结尾的字符串最长不含重复子串的长度
        int[] dp = new int[s.length() + 1];
        for(int i = 1; i <= s.length(); i++){
            dp[i] = 1;
            //哈希表中没有,说明不重复
            if(!mp.containsKey(s.charAt(i - 1)))
                //前一个加1
                dp[i] = dp[i - 1] + 1;
            //遇到重复字符
            else
                dp[i] = Math.min(dp[i - 1] + 1, i - mp.get(s.charAt(i - 1)));
            //加入哈希表
            mp.put(s.charAt(i - 1), i);
            //维护最大值
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

复杂度分析:

  • 时间复杂度:O(n)O(n),其中nn为字符串长度,遍历一次字符串
  • 空间复杂度:O(n)O(n),辅助数组dp的大小为字符串长度,哈希表的最大空间为字符串长度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值