940. 不同的子序列 II

940. 不同的子序列 II

问题:给定一个字符串 s,计算 s 的 不同非空子序列 的个数。因为结果可能很大,所以返回答案需要对 10^9 + 7 取余 。

字符串的 子序列 是经由原字符串删除一些(也可能不删除)字符但不改变剩余字符相对位置的一个新字符串。

例如,“ace” 是 “abcde” 的一个子序列,但 “aec” 不是。

思路:

  1. 先思考一个不同字母字符串的所有子序列。 如 “abc"
    陆续以不同的字符作为子序列的结尾,从左到右可得

遍历到a 子序列: a
遍历到b 子序列:a ab b
遍历到c 子序列:a ab b ac abc bc c

很明显可以看出,dp[i] = dp[i-1] + new + 1
new即是上一个所有的子字符串后面加上这个新的字母,1即是自己本身这个新增的字母

  1. 那现在思考如果该字符串有重复的字母。
    重复的字母就意味着会出现重复的子序列,根据上面可很容易想到,在最新的dp[i]中,只有new和1(自己)是可能存在重复的子序列的。如"abaa"

遍历到第一个a 子序列: a
遍历到第一个b 子序列:a + ab + b
遍历到第二个a 子序列:a ab b + aa aba ba + a 其中new中
遍历到第三个a 子序列:a ab b aa aba ba + aa aba ba aaa abaa baa + a

可以看出来,

第一个a:新增了子序列为a,重复子序列无
第二个a:新增子序列 aa aba ba a,重复子序列为a
第三个a:新增子序列 aa aba ba aaa abaa baa a ,重复子序列为aa aba ba a

很明显,如果你遍历到了第三个a,new会把前面所有的子序列后都加个a,而这其中重复的都是第二个a中新增的子序列。
很好理解,因为当遍历到第三个a时,肯定已经拥有了当遍历到第二个a时的所有子序列,new是把这些子序列尾部加a,就相当于把它做过的事情再做一遍,这一部分必定是重复的。
同时,你自己本身字母作为子序列也是重复的

dp[i] = dp[i-1] + new + 1 - repeat
其中new即是dp[i-1]的子序列后尾部新增最新字母,两个数值一样

所以要初始化一个长度为26的int数组,用于存放每个字母的repeat子序列,也就是每个字母的新增子序列。也就是new + 1。

class Solution {
    public int distinctSubseqII(String s) {

        int[] repeatArr = new int[26];

        char[] arr = s.toCharArray();

        // 记录子序列数目
        int count = 0;

        int mod = (int)(1e9 + 7);

        // 记录重复子序列数目
        int repeat = 0;

        // 2.遍历
        for (int i = 0; i < s.length(); i++) {
            // repeatCount
            int j = arr[i] - 'a';
            repeat = repeatArr[j];
            // 记录该字母新增的子序列(最新)
            repeatArr[j] = (count+1)%mod;
            count = ((2 * count)%mod - repeat + 1 + mod)%mod;
        }
        return count;
    }
}

运行结果:
在这里插入图片描述

总结:类似问题先举例简单字符串,一步步抽丝剥茧,发现类似规律后一击必杀

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

范大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值