【Lintcode】1702. Distinct Subsequences II(配数学证明)

题目地址:

https://www.lintcode.com/problem/distinct-subsequences-ii/description

给定一个长 n n n的字符串 s s s,问其所有不同的非空子序列一共多少个。

思路是动态规划。容易想到按照子序列的结尾分类,这里的关键是如何去重。我们先写出 f f f的递推式,然后解释其正确性: f [ i ] = 1 + ∑ s [ j ] ≠ s [ i ] , j = 0 , 1 , . . . , i − 1 f [ j ] f[i]=1+\sum_{s[j]\ne s[i],j=0,1,...,i-1}f[j] f[i]=1+s[j]=s[i],j=0,1,...,i1f[j]最后答案就是 ∑ f \sum f f。这里累加的时候,略过了与 s [ i ] s[i] s[i]相等的字符对应的 f f f。如果 ∄ j < i , s [ j ] = s [ i ] \nexists j<i,s[j]=s[i] j<i,s[j]=s[i],那 f [ i ] = 1 f[i]=1 f[i]=1,这里的 1 1 1计数的就是 s [ 0 : i ] s[0:i] s[0:i]这个字符串;设 s [ i ] s[i] s[i] s [ 0 : i − 1 ] s[0:i-1] s[0:i1]出现的所有位置分别是 j 1 , . . . , j k j_1,...,j_k j1,...,jk j k < . . . < j 1 j_k<...<j_1 jk<...<j1,那么对于 j 1 < k < i j_1<k<i j1<k<i,累加 f [ k ] f[k] f[k]所计数的子序列就是 f [ k ] f[k] f[k]所计数的子序列后面接上 s [ i ] s[i] s[i],而对于 j 2 < k < j 1 j_2<k<j_1 j2<k<j1,累加 f [ k ] f[k] f[k]所计数的子序列就是 f [ k ] f[k] f[k]所计数的子序列后面接上 s [ i ] × 2 s[i]\times 2 s[i]×2,以此类推。举例如下:对于 s = a b a b s=abab s=abab,那么 f [ 0 ] = 1 f[0]=1 f[0]=1对应的是 a a a f [ 1 ] = 2 f[1]=2 f[1]=2对应的是 b b b a b ab ab,而 f [ 2 ] = 1 + f [ 1 ] = 3 f[2]=1+f[1]=3 f[2]=1+f[1]=3对应,这里 1 1 1指的是 a a aa aa,而 f [ 1 ] f[1] f[1]累加的是 b a ba ba a b a aba aba,接着 f [ 3 ] = 1 + f [ 2 ] + f [ 0 ] = 5 f[3]=1+f[2]+f[0]=5 f[3]=1+f[2]+f[0]=5,这里 1 1 1指的是 b b bb bb,而 f [ 2 ] f[2] f[2]累加的是之前 f [ 2 ] f[2] f[2]所代表的 a a aa aa b a ba ba a b a aba aba后面接上 s [ 3 ] = b s[3]=b s[3]=b,即 a a b aab aab b a b bab bab a b a b abab abab,而 f [ 0 ] f[0] f[0]累加的是之前 f [ 0 ] f[0] f[0]所代表的 a a a后面接上 b × 2 b\times 2 b×2,即 a b b abb abb。列在下面:
f [ 0 ] f[0] f[0] a a a
f [ 1 ] f[1] f[1] b b b a b ab ab
f [ 2 ] f[2] f[2] a a aa aa b a ba ba a b a aba aba
f [ 3 ] f[3] f[3] b b bb bb a a b aab aab b a b bab bab a b a b abab abab a b b abb abb
最后答案就是 11 11 11

我们证明一下上面的算法的正确性:

首先容易看出,任何一个子序列一定会被枚举到(这里可以这样看,任何一个子序列都会在它第一次出现的位置被枚举到,这里的“第一次”出现指的是每个字符都取尽量左边的那个)。接下来只需要证明没有重复即可。这一点可以用数学归纳法证明。对 s s s的长度做归纳。我们要证明的结论是,每个子序列会在它第一次出现的位置被枚举到(这句话的意思是它第一次出现的时候,它的最后一个字符所在位置 i i i对应的 f [ i ] f[i] f[i]会对其进行计数),并且之后不会重复枚举。当长度为 1 , 2 1,2 1,2时结论正确。设长度小于 n n n的情况下结论也正确,当长度等于 n n n的时候。如果有重复,接下来分类讨论。如果最后一个重复的位置是相同的,由归纳假设,前面部分只会在第一次出现的时候枚举到,这就矛盾了;如果最后一个重复的位置不同,一个在 c = s [ n − 1 ] c=s[n-1] c=s[n1],另一个在 c = s [ m ] c=s[m] c=s[m]并且 m < n − 1 m<n-1 m<n1,那么将两个子序列同时去掉最后一个字符,前面的部分,由归纳假设,都是在第一次出现的位置被枚举的,但是前面部分的最后一个字符的下标是小于 m m m的,在算 f [ n − 1 ] f[n-1] f[n1]的时候枚举不到这个子序列,因为算 f [ n − 1 ] f[n-1] f[n1]的时候枚举到的子序列事实上是上面子序列去掉最后一个字符后后面至少接两个 c c c,这就矛盾了。所以结论正确。

事实上上面的 f [ i ] f[i] f[i]存的就是,所有以 s [ i ] s[i] s[i]结尾的,并且是第一次出现的子序列的个数。

代码如下:

public class Solution {
    /**
     * @param S: The string s
     * @return: The number of distinct, non-empty subsequences of S.
     */
    public int distinctSubseqII(String S) {
        // Write your code here
        int res = 0, MOD = (int) (1E9 + 7);
        int[] dp = new int[S.length()];
        for (int i = 0; i < S.length(); i++) {
            dp[i] = 1;
            for (int j = 0; j < i; j++) {
                if (S.charAt(j) != S.charAt(i)) {
                    dp[i] += dp[j];
                    dp[i] %= MOD;
                }
            }
        }
        
        for (int i : dp) {
            res += i;
            res %= MOD;
        }
        
        return res;
    }
}

时间复杂度 O ( n 2 ) O(n^2) O(n2),空间 O ( n ) O(n) O(n)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值