【GDOI2016】最长公共子串 题解

来源:互联网 发布:小凯老师淘宝 编辑:程序博客网 时间:2024/05/01 20:46

Description

给你两个字符串S,T
给你N个区间,S串中这些区间内的字符可以随意改变在区间中的位置。
S,T可以达到的的最长公共子串长度

Solution

注意,公共子串是必须连着的(坑死我了

可以发现,只要两个区间有可以重叠的地方,就可以把这两个区间合并成一个区间。

那么就可以把原本100000的区间个数降到2000以下

如果一个位置没有被任何区间覆盖,那么我们就把它单独作为一个区间。

我们需要记录一下每个区间中每种字符的个数。

找到这个就可以开始DP了

f[i][j]表示S串中从第i个区间开始,T串中从第j个字符开始的最长公共子串的长度。

g[i]表示第i个区间的长度

然后我们可以暴力判断T[j]~T[j+g[i]1]这一段中的每个字符是否在对应的S船这个区间内足够。

如果当前搜到第k个不足够,那就使f[i][j]=k1

如果全部都可以的就使f[i][j]=k+f[i+1][j+k]

最后,对于每个i,j,还有可能T[jp]~T[j1]S串中的第i1个区间是可以的,那么我们也把这一部分暴力做一遍就可以了

Code

#include<cstdio>#include<cstdlib>#include<algorithm>#include<iostream>#include<cstring>#include<cmath>#define fo(i,a,b) for(i=a;i<=b;i++)#define fod(i,a,b) for(i=a;i>=b;i--)using namespace std;int al[2005][2],alp[2005][26],lt[26],bz[2005],f[2005][2010];char s[2005],t[2005];int lens,lent,n,m,ans;int main(){    freopen("lcs.in","r",stdin);    freopen("lcs.out","w",stdout);    cin>>t+1;    scanf("\n");    cin>>s+1;    scanf("\n");    lens=strlen(s+1);    lent=strlen(t+1);    int i,j,k;    cin>>n;    fo(i,1,n)    {        int x,y;        scanf("%d%d",&x,&y);        bz[x+1]++;        bz[y+1]--;    }    j=0;    m=0;    fo(i,1,lens+1)    {        if (j==0&&j+bz[i]>0)al[++m][0]=i;        else if(j>0&&j+bz[i]==0)al[m][1]=i;        if(j+bz[i]==0&&j==0&&i!=lens+1) al[++m][0]=al[m][1]=i;        j+=bz[i];        if (i!=lens+1) alp[m][s[i]-97]++;    }    fod(i,m,1)    {        int lg=al[i][1]-al[i][0]+1;        fod(j,lent,1)        {            memset(lt,0,sizeof(lt));            fo(k,1,lg)            {                lt[t[j+k-1]-97]++;                if (lt[t[j+k-1]-97]>alp[i][t[j+k-1]-97])                {                    f[i][j]=k-1;                    break;                }                if (k==lg||j+k>lent)                 {                    f[i][j]=k+f[i+1][j+k];                    break;                }            }            ans=max(ans,f[i][j]);        }    }    fo(i,2,m)    {        int lg=al[i][1]-al[i][0]+1;        fo(j,2,lent)        {            memset(lt,0,sizeof(lt));            for(k=1;j-k>=1&&al[i][1]-k>=al[i-1][0];k++)            {                lt[t[j-k]-97]++;                if (lt[t[j-k]-97]>alp[i-1][t[j-k]-97])                {                    ans=max(ans,k-1+f[i][j]);                    break;                }                ans=max(ans,k+f[i][j]);            }        }    }    cout<<ans;}
0 0