NOIP2015 子串 dp

来源:互联网 发布:adobe pdf 电脑软件 编辑:程序博客网 时间:2024/06/05 16:50

【问题描述】

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

【输入格式】

  第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问题描述中所提到的 k,每两个整数之间用一个空格隔开。 第二行包含一个长度为 n 的字符串,表示字符串 A。 第三行包含一个长度为 m 的字符串,表示字符串 B。

【输出格式】

  输出共一行,包含一个整数,表示所求方案数。由于答案可能很大,所以这里要求输出答案对 1,000,000,007 取模的结果。

【输入样例】

【样例1】
 6 3 1
 aabaab
 aab

【样例2】
 6 3 2
 aabaab
 aab

【样例3】
 6 3 3
 aabaab
 aab

【输出样例】

【样例1】
 2

【样例2】
 7

【样例3】
 7

【数据范围】

对于第 1 组数据:1≤n≤500,1≤m≤50,k=1;
对于第 2 组至第 3 组数据:1≤n≤500,1≤m≤50,k=2;
对于第 4 组至第 5 组数据:1≤n≤500,1≤m≤50,k=m;
对于第 1 组至第 7 组数据:1≤n≤500,1≤m≤50,1≤k≤m;
对于第 1 组至第 9 组数据:1≤n≤1000,1≤m≤100,1≤k≤m;
对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m。

——————————————————————————————————————————————————————

第一要务,读懂题意然后方程就不会连样例都过不了了(不要问我为什么会这么说)。
这玩意儿算是把匹配和子集弄在一起了?
f(i,j,x)表示从A的前i个字符中选出x个不重叠的字符串拼接起来和B的前j个字符完全一样的方案数:
f(i,j,x)=f(i-1,j,x) + ( A[ii]==B[jj] ? f(ii-1,jj-1,x-1) : 0 ) , ii<=i,jj<=j , f(i,0,0)=1;
这样写下来就有个70分的样子了,复杂度O(N * M^2 * K),显然想要AC还是有点距离。

注意到方程中加号后面实际上是连续一串f的和(脑补一下状态表),所以就需要预处理一下,same[i][j]表示A的第i个字符和B的第j个字符作为结尾的最长连续子串的长度,sum[i][j][x]=f[i-1][j-1][x]+f[i-2][j-2][x]+……f[i-min(i,j)][j-min(i,j)][x],然后就可以把用前缀和把复杂度优化为 O(N * M * K),AC get!
same[i][j]= A[i]==B[j] ? same[i-1][j-1]+1 : 0
f(i,j,x)=f(i-1,j,x) + ( A[i]==B[j] ? sum[i-1][j-1][x-1]-sum[i-same[i][j]][j-same[i][j]][x-1]+f(i-same[i][j],j-same[i][j],x-1) : 0 );

注意到sum的下标如果写成sum[i-same[i][j]-1][j-same[i][j]-1][x-1]是有可能小于0的,所以保险起见写成上面的形式。

如果有更好的做法求求你告诉我ORZ!!!

AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>using namespace std;const int maxn=1005;const int maxm=205;const int mo=1000000007;char A[maxn],B[maxm];int N,M,K,f[maxn][maxm][maxm];int same[maxn][maxm],sum[maxn][maxm][maxm];void data_in(){    scanf("%d%d%d\n",&N,&M,&K);    gets(A+1);gets(B+1);}void ready(){    for(int i=1;i<=N;i++)    for(int j=1;j<=M;j++)        same[i][j]= A[i]==B[j] ? same[i-1][j-1]+1 : 0;}void dp()//O(N*K*M){    for(int i=0;i<=N;i++) f[i][0][0]=1;    for(int i=1;i<=N;i++)    for(int x=1;x<=min(K,i);x++)    {        int upper=min(i,M);        for(int j=1;j<=upper;j++)        {            f[i][j][x]=f[i-1][j][x];            if(A[i]==B[j])                f[i][j][x]=(((f[i][j][x]+sum[i-1][j-1][x-1])%mo                           -sum[i-same[i][j]][j-same[i][j]][x-1]+mo)%mo                           +f[i-same[i][j]][j-same[i][j]][x-1])%mo;            sum[i][j][x]=(sum[i-1][j-1][x]+f[i][j][x])%mo;        }    }}void work(){    ready();    dp();    printf("%d\n",f[N][M][K]);}int main(){    freopen("test.in","r",stdin);    freopen("test.out","w",stdout);    data_in();    work();    return 0;}
原创粉丝点击