GDOI 2016 Day1 T2 最长公共子串

Description

给出两个字符串A和B,求最长公共子串。
其中B串中有k个区间的字符可以任意调换。
|A|,|B|<=2000,k<=100000

Solution

首先,一个很明显的性质,两个区间如果有交集,那么这两个区间可以合并成一个。
然后,k就可以降到2000级别了。
开始乱搞。
你可以选择双指针往后推,也可以使用DP。
这里介绍后者。
设f[i,j]表示以A串的第i位和B串的第j位结尾的最长公共子串。
则有两种情况。
一是这个区间中的东西够用。
处理出A串的前缀和和B串每个区间的字符个数即可。
二是这个区间有东西,但不够。
那么我们可以减少一个这个位子的字符的使用。
处理出A串每个位置往后第一个字符的位置即可。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 2005
using namespace std;
struct note{int l,r;}ask[102005];
bool cmp(note x,note y) {return x.l<y.l;}
char s[N],st[N];
int n,m,l,r,k,ans,a[N],b[N],sum[N][26],c[N][26];
int f[N][N],left[N],next[N][26],cnt[26];
int main() {
    freopen("lcs.in","r",stdin);
    freopen("lcs.out","w",stdout);
    scanf("%s",s+1);n=strlen(s+1);fo(i,1,n) a[i]=s[i]-'a';
    scanf("%s",st+1);m=strlen(st+1);fo(i,1,m) b[i]=st[i]-'a';
    scanf("%d",&k);
    fo(i,1,k) scanf("%d%d",&ask[i].l,&ask[i].r),ask[i].l++,ask[i].r++;
    fo(i,k+1,k+m) ask[i].l=ask[i].r=i-k;k+=m;
    sort(ask+1,ask+k+1,cmp);l=ask[1].l;r=ask[1].r;
    fo(i,2,k) if (ask[i].l>r) {
        fo(j,0,25) cnt[j]=0;
        fo(j,l,r) left[j]=l,cnt[b[j]]++;
        fo(j,l,r) fo(t,0,25) c[j][t]=cnt[t];
        l=ask[i].l;r=ask[i].r;
    } else r=max(r,ask[i].r);
    fo(j,0,25) cnt[j]=0;
    fo(j,l,r) left[j]=l,cnt[b[j]]++;
    fo(j,l,r) fo(t,0,25) c[j][t]=cnt[t];
    fo(i,1,n) {
        fo(j,0,25) sum[i][j]=sum[i-1][j];
        sum[i][a[i]]++;
    }
    fd(i,n,1) {
        fo(j,0,25) next[i][j]=next[i+1][j];
        next[i][a[i]]=i;
    }
    fo(i,1,n)
        fo(j,1,m) {
            int x=a[i]; 
            k=max(i-f[i-1][j-1],i-j+left[j]);
            if (sum[i-1][x]-sum[k-1][x]<c[j][x]) f[i][j]=f[i-1][j-1]+1;
            else if (next[k][x]&&next[k][x]<i&&c[j][x]) f[i][j]=i-next[k][x];
            ans=max(ans,f[i][j]);
        }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值