D2T2 子串

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( lenalenb )当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[i1][j]+Σdp[ip][jp],p[1,lcp[i][j] 。01背包的思想,A中第i个用或不用。复杂度是 O(m2n)

正解就是在暴力的基础上优化,因为子串数目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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值