您的位置:首页 > 其它

GDOI2016 Day1 T2 最长公共子串

2016-05-14 16:25 246 查看

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
,st
;
int n,m,l,r,k,ans,a
,b
,sum
[26],c
[26];
int f

,left
,next
[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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: