LCR 167. 招式拆解 I

解题思路:

动态规划+哈希表

dp[ j ]指的是以字符s[ j ]结尾的字符串的长度。这个字符串不能含有重复字符。

我们可以记为字符串sub[ j ], 该字符串以字符s[ j ]结尾,长度为dp[ j ]。

这样就比较好理解状态转移方程了。固定右边界 j ,同时定义从边界 j 往左侧距离最近的相同字符的索引为 i 。

以字符s[ j - 1 ]结尾的字符串记录为sub[ j - 1],其长度为dp[ j - 1 ],注意sub [ j - 1]中字符不重复。 我们看索引 j 的情况:在 j 的左侧寻找一个重复的字符s [ i ],那么索引 i 可能在字符串sub[ j - 1]的区间内,也可能在字符串sub[ j - 1]的左边界更左侧才有可能寻找到。这样就需要分两种情况考虑。

如果字符 s[ i ] 在子字符串 sub[ j − 1 ] 区间之外,也就是更左侧, 那么dp[ j - 1 ] < j - i,这种情况下,由于sub [ j - 1]中字符不重复,且当前最长,所以以s[ j ]为结尾的字符串只需要在子字符串 sub[ j − 1 ]后面接上这个字符s[ j ]就可以了,其长度dp [ j ] = dp[ j - 1 ] + 1;

如果字符 s[ i ] 在子字符串 sub[ j− 1 ] 区间之中,必然dp[ j−1 ] ≥ j − i,这种情况下,由于我们需要寻找以s[ j ]结尾且最长的字符串,那么sub[ j ]的左边界只能是 i 了,其长度 dp[ j ] = j − i 。

下面举个例子,比如“abcdbaa”,索引从0开始。 我们容易得到,当 j = 4时,以s[4]结尾字符串sub[4] = “cdb”的 长度dp[4] =3。 接下来我们看 j +1的情况。根据定义,sub[4]字符串中的字符肯定不重复,所以当 j = 5时,这时距离字符s[5]的左侧的重复字符a的索引 i = 0, 也就是说s[ 0 ]在子字符串sub[ 4 ]之外了,以s[5]结尾的字符串自然在sub[4]的基础上加上字符s[5]就构成了新的最长的不重复的子串sub[5],长度dp[5] = dp[4] + 1; 接下来我们继续看 j =6的情况,这时s[6]的左侧重复字符a的索引 i = 5,该重复字符在sub[ 5 ]中。新的最长不重复的字串sub[6]左边界以 i 结尾,长度dp[6] = j - i = 1。

abcdbaa

0123456

前置知识:

int i = dic.getOrDefault(arr.charAt(j), -1);

在dic中查找arr.charAt(j),如果存在返回索引,不存在就返回-1(第二个参数设定的值)

dic 这个名字是 “dictionary”(字典)的缩写,字典是一个常见的数据结构,允许以键-值对的形式存储和检索数据。在 Java 中,HashMap 实现了类似字典的功能,因此这里的 dic 就是被用作字典(键-值对映射)来追踪字符和他们最后一次出现的索引。

class Solution {
    public int dismantlingAction(String arr) {
        Map<Character, Integer> dic = new HashMap<>();
        int res = 0;
        int len = arr.length();
        int[] dp = new int[len];
        
        for (int j = 0; j < len; j++) {
            // 获取dic中对应键的值,即索引 i
            int i = dic.getOrDefault(arr.charAt(j), -1); 
            // 更新哈希表,保证字符的索引始终在最新出现的相同字符的位置上,如babce,b的索引先是0,后是2
            dic.put(arr.charAt(j), j); 
            
            if (j == 0) {
                dp[j] = 1; // 初始化dp[0]
            } else if (dp[j - 1] < j - i) {
                dp[j] = dp[j - 1] + 1; // dp[j - 1] -> dp[j]
            } else {
                dp[j] = j - i; // 新的子字符串开始的位置
            }
            
            res = Math.max(res, dp[j]); // 更新结果
        }
        return res; // 返回最长不重复子串的长度
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值