题目
题目链接:https://gmoj.net/senior/#main/show/4486
(n,mleq 2000,kleq 10^5)。
思路
首先如果两个段有交,那么可以把这两个段合并起来。
然后对于 (S) 串中任意一个字符 (c),如果它没有被任何区间覆盖,那么可以再加入一个长度为 (1) 的区间覆盖它。
然后考虑 dp。设 (f[i][j]) 表示 (S) 串的第 (i) 个区间与 (T) 串的第 (j) 个字符开始,往后最多能匹配的位数。这个可以直接枚举求出,用桶来维护是否可行。
再设 (g[i][j]) 表示 (S) 串第 (i) 个区间到最后一个区间,与 (T) 串位置 (j) 开始,且这 (f[i][j]) 个字符必须全部匹配时,往后能最大匹配的位数。只需要判断一下第 (i+1) 个区间是否全部匹配上 (T) 即可。
然后枚举第 (i) 个区间匹配的长度求最大值即可。
时间复杂度 (O(nm))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=2010,M=110010,Inf=1e9;
int n,m,k,ans,cnt,bel[N],c[N][27],d[27],f[N][N],g[N][N];
char s[N],t[N];
struct node
{
int l,r;
}a[M];
bool cmp(node x,node y)
{
return x.l<y.l;
}
int main()
{
freopen("lcs.in","r",stdin);
freopen("lcs.out","w",stdout);
scanf("%s%s%d",t+1,s+1,&k);
n=strlen(s+1); m=strlen(t+1);
for (int i=1;i<=k;i++)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].l++; a[i].r++;
}
for (int i=1;i<=n;i++)
a[++k]=(node){i,i};
sort(a+1,a+1+k,cmp);
for (int i=1;i<=k;i++)
if (a[i].l<=a[cnt].r) a[cnt].r=max(a[cnt].r,a[i].r);
else a[++cnt]=a[i];
k=cnt;
for (int i=1;i<=k;i++)
for (int j=a[i].l;j<=a[i].r;j++)
bel[j]=i,c[i][s[j]-'a']++;
for (int i=k;i>=1;i--)
for (int j=m;j>=1;j--)
{
int len=a[i].r-a[i].l,l=j;
for (;l<=min(j+len,m);l++)
{
if (c[i][t[l]-'a']==d[t[l]-'a']) break;
d[t[l]-'a']++;
}
f[i][j]=l-j;
if (f[i+1][l]==a[i+1].r-a[i+1].l+1)
g[i][j]=f[i][j]+g[i+1][l];
else
g[i][j]=f[i][j]+f[i+1][l];
for (int p=j;p<=l;p++)
{
if (p!=l) d[t[p]-'a']--;
if (f[i+1][p]==a[i+1].r-a[i+1].l+1)
ans=max(ans,p-j+g[i+1][p]);
ans=max(ans,p-j+f[i+1][p]);
}
}
cout<<ans;
return 0;
}