[NOIP2015提高组]子串 DP

时间:2022-12-17 20:44:40

题目描述

有两个仅包含小写英文字母的字符串 A 和 B。现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一 个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?注意:子串取出 的位置不同也认为是不同的方案。

题解:

简单DP。状态表示以及转移见代码。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=1010,Maxm=210,Maxk=210,mod=1e9+7;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*f;
}
int n,m,K,pos[26][Maxm],len[26];
char A[Maxn],B[Maxm];
int f[Maxm][Maxk],g[2][Maxm][Maxk];
//f[j][k] B串匹配到j 分了k段 的方案数
//g[j][k] B串匹配到j 分了k段 且上一位有选的方案数 
int main()
{
    n=read();m=read();K=read();
    scanf("%s%s",A+1,B+1);
    for(int i=1;i<=m;i++)pos[B[i]-'a'][++len[B[i]-'a']]=i;
    f[0][0]=1;
    int now=0;
    for(int i=1;i<=n;i++)
    {
        int x=A[i]-'a';now^=1;
        memset(g[now],0,sizeof(g[now]));
        for(int j=1;j<=len[x];j++)
        {
            int p=pos[x][j],lim=min(p,K);//匹配到p 
            for(int k=1;k<=lim;k++)//分了k段 
            g[now][p][k]=(f[p-1][k-1]+g[now^1][p-1][k])%mod; //新分一段 和前面的一段合在一起 
        }
        int lim1=min(i,m),lim2=min(i,K);
        for(int j=0;j<=lim1;j++)
        for(int k=0;k<=lim2;k++)
        f[j][k]=(f[j][k]+g[now][j][k])%mod;
    }
    printf("%d",f[m][K]);
}