Task
:
从A中取出K个互不重叠的非空子串,顺序链接构成B串的方案数。
取出位置不同认为是不同的方案。
对于第 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。
Solution
① 当
K=1
时,问题转化为:A中某个子串与B匹配的方案数。对AB求
LCP
(最长公共前缀),复杂度为O(
lena∗lenb
)当A中i位置的LCP为B的长度时,方案数+1.
② 当
K=lenb
时,问题转化为:只删除A,求A,B的编辑距离。
两个字符串匹配的问题,可以用dp来实现,在编辑距离中:令
dp[i][j]
表示A中前i个与B中前j个匹配的方案数。
因为B是不能改变的,所以B中[1,j]必须使用,A中[1,i]选择使用。
转移方程:
dp[i][j]=dp[i−1][j]+Σdp[i−p][j−p],p∈[1,lcp[i][j]
。01背包的思想,A中第i个用或不用。复杂度是
O(m2∗n)
。
正解就是在暴力的基础上优化,因为子串数目K只从K-1转移,可以滚动数组优化,转移的是一个区间的值,可以前缀和优化。
此题给我的启发:先从暴力开始想题目,再考虑优化。如果30min内没有想出正解,一定要先打暴力。
const int N=203,M=1003,P=1e9+7;
char A[N],B[M];
int dp[2][N][M],sum[2][N][M];
int lena,lenb,n;
inline void input(){
rd(lenb);rd(lena);rd(n);
scanf("%s %s",B+1,A+1);
}
inline void add(int &x,int y){
x+=y;
if(x>=P)x-=P;
}
inline void DP(){
int cur=0;
rep(i,0,lenb){
dp[0][0][i]=1;
sum[0][0][i]=1;
}
rep(i,1,lena)
rep(j,1,lenb)
if(A[i]==B[j])sum[0][i][j]=sum[0][i-1][j-1];
rep(k,1,n){
cur^=1;
memset(dp[cur],0,sizeof(dp[cur]));
memset(sum[cur],0,sizeof(sum[cur]));
rep(i,1,lena)
rep(j,1,lenb){
dp[cur][i][j]=dp[cur][i][j-1];//不用j
if(A[i]==B[j]){
add(dp[cur][i][j],sum[cur^1][i-1][j-1]);
sum[cur][i][j]=sum[cur][i-1][j-1];
}
add(sum[cur][i][j],dp[cur][i][j]);
}
}
printf("%d\n",dp[cur][lena][lenb]);
}
int main(){
input();
DP();
return 0;
}