牛客练习赛60C-操作集锦-(dp)

https://ac.nowcoder.com/acm/contest/4853/C

求长度为n的字符串的 长度为k的不同子序列。

dp[i][j]表示前i个字符串,长度为j的子序列数量。

不计算重复,对于第j个字符,dp[i][j]=dp[i-1][j]+dp[i-1]+dp[j-1];前者不用上第j个字符,后者用上第j个字符。

去重,对于当前字符x,last[x]表示上一次x出现的位置,去重部分dp[ pre[x]-1 ][j-1]。

这个去重理解了挺久,纸上比划一下才清楚,例如有字符串abcdde(下标1-6),j=2

i=4时,x=d,dp[4][2]=dp[3][2]+dp[3][1];

i=5时,x=d,dp[5][2]=dp[4][2]+dp[4][1]-dp[3][1];

上一个d在i=4的位置,需要把 用i=4的d 的情况 去掉,也就是dp[3][1]。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define inf 0x3f3f3f3f
const double pi=3.1415926;
const double eps=1e-9;
const ll p=1e9+7;
using namespace std;

ll dp[1005][1005];
ll last[30];
char a[1005];
int n,k;

int main()
{

    scanf("%d %d",&n,&k);
    getchar();
    scanf("%s",a+1);
    dp[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        int x=a[i]-'a';
        dp[i][0]=1;
        for(int j=1;j<=i;j++)
        {
            dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
            if(last[x]!=0)
                dp[i][j]-=dp[ last[x]-1 ][j-1];
            dp[i][j]=dp[i][j]%p;
        }
        last[x]=i;
    }
    if(dp[n][k]<0)
        dp[n][k]+=p;///
    printf("%lld
",dp[n][k]);
    return 0;
}
原文地址:https://www.cnblogs.com/shoulinniao/p/12592546.html