String

14 篇文章 0 订阅

Description

有两种字符串S,T。长度分别为n,m。现在需要在S里面有序地选出k个子串,且在T中出现的顺序与这k个子串的顺序相同。问这k个子串最大的长度和

Input

第一行三个数n,m,k
第二行长度为n的S串
第三行长度为m的T串

Output

一个数表示答案

Sample Input

9 12 4
bbaaababb
abbbabbaaaba

Sample Output

7

Data Constraint

30% n<=10,m<=20
100% n,m<=1000 k<=10

分析

一个显然的思路:设f[i][j][k]表示S串匹配到了第i位,j串匹配到了第j位,取了k个子串的最大值。转移:
f[i][j][k]=max{f[i’][j’][k-1]+len}
其中len是要枚举的,i’≤i-len,j’≤j-len。还要判断S[i-len+1,i]与T[j-len+1,j]是否匹配。
这个转移显然是 O(n3m2k)

优化

对于一个len,枚举的i’和j’可以看成一个矩形,然后可以设 s[i][j][k]=maxf[i][j][k] ,求这个的总复杂度是 O(nmk) (s[i][j][k]=max{s[i][j-1][k],s[i-1][j][k],f[i][j][k]})
然后上面的方程就变成f[i][j][k]=max{s[i-len][j-len][k-1]+len}

进一步优化

我们发现转移的i-len与j-len的差等于i-j,所以对于相同的i-j和k,把所有s[i][j][k]放在一个单调队列里。时间复杂度降至 O(nmk)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int maxn=1005;

int n,m,k,s[maxn][maxn][11],data[maxn*2][maxn][10],h[maxn*2][10],t[maxn*2][10],p[maxn][maxn];

char S[maxn],T[maxn];

int main()
{
    freopen("string.in","r",stdin); freopen("string.out","w",stdout);
    scanf("%d%d%d%s%s",&n,&m,&k,S+1,T+1);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++) if (S[i]==T[j]) p[i][j]=p[i-1][j-1]+1;
    memset(t,255,sizeof(t));
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            int x=i-j+m,f;
            for (int T=k-1;T;T--)
            {
                for (;h[x][T-1]<=t[x][T-1] && i-data[x][h[x][T-1]][T-1]>p[i][j];h[x][T-1]++);
                f=(h[x][T-1]<=t[x][T-1])?s[data[x][h[x][T-1]][T-1]][data[x][h[x][T-1]][T-1]-i+j][T-1]+i-data[x][h[x][T-1]][T-1]:0;
                s[i][j][T]=max(max(s[i-1][j][T],s[i][j-1][T]),f);
                for (;h[x][T]<=t[x][T] && s[data[x][h[x][T]][T]][data[x][h[x][T]][T]-i+j][T]+i-data[x][h[x][T]][T]<=s[i][j][T];h[x][T]++);
                data[x][++t[x][T]][T]=i;
            }
            f=p[i][j];
            s[i][j][0]=max(max(s[i-1][j][0],s[i][j-1][0]),p[i][j]);
            for (;h[x][0]<=t[x][0] && s[data[x][h[x][0]][0]][data[x][h[x][0]][0]-i+j][0]+i-data[x][h[x][0]][0]<=s[i][j][0];h[x][0]++);
            data[x][++t[x][0]][0]=i;
        }
    int ans=0;
    for (int i=0;i<k;i++) ans=max(ans,s[n][m][i]);
    printf("%d\n",ans);
    fclose(stdin); fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值