GDOI 2016 Day1 T2 最长公共子串

来源:互联网 发布:身份证复印件软件下载 编辑:程序博客网 时间:2024/05/22 00:28

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 2005using 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);}
0 0
原创粉丝点击