1. 程式人生 > >【[CTSC2012]熟悉的文章】

【[CTSC2012]熟悉的文章】

題目

好題啊

\(SAM\)+單調佇列優化\(dp\)

首先這個\(L\)滿足單調性真是非常顯然我們可以直接二分

二分之後套一個\(dp\)就好了

\(dp[i]\)表示到達\(i\)位置熟悉的文章的最大長度

有一個非常顯然的\(dp\)方程

\[dp_i=max\{dp_j+i-j\}\ (i-j>=mid)\]

同時\([j+1,i]\)這個子串也得是模式串裡的一個子串

對於上面那個\(dp\)方程,我們把\(i\)提出來,用單調佇列維護一下\(dp_j-j\)的最大值就好了

下面這個限制條件的話,我們可以處理出以\(i\)這個位置的為結尾的和所有的模式串的最長公共子串的長度\(mx[i]\)

這個處理的話我們直接在\(SAM\)上過一遍就好了

之後我們就有了第二個限制

\[j>=i-mx[i]\]

經過分析我們可以發現\(i\)每次穩定加\(1\),而\(mx[i]\)每次最多加\(1\),所以\(i-mx[i]\)是單調不降的的

於是我們還是可以用單調佇列來維護

複雜度\(O(Tnlogn)\)

程式碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 2000010
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
char S[maxn];
int fa[maxn<<1],len[maxn<<1],son[maxn<<1][3];
int mx[maxn];
int q[maxn],dp[maxn];
int T,m,n,cnt=1,lst=1;
inline void ins(int c)
{
    int f=lst,p=++cnt; lst=p;
    len[p]=len[f]+1;
    while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    if(!f) {fa[p]=1;return;}
    int x=son[f][c];
    if(len[f]+1==len[x]) {fa[p]=x;return;}
    int y=++cnt;
    len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
    for(re int i=0;i<3;i++) son[y][i]=son[x][i];
    while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
inline void query()
{
    int now=1,L=0;
    for(re int i=1;i<=n;i++)
    {
        if(son[now][S[i]-'0']) {mx[i]=++L;now=son[now][S[i]-'0'];continue;}
        while(now&&!son[now][S[i]-'0']) now=fa[now];
        if(!now) {mx[i]=L=0;now=1;continue;}
        L=len[now]+1,mx[i]=L;now=son[now][S[i]-'0'];
    }
}
inline int check(int mid)
{
    int h=1,t=0;
    for(re int i=0;i<=n;i++) q[i]=dp[i]=0;
    for(re int i=mid;i<=n;i++)
    {
        while(h<=t&&dp[i-mid]-i+mid>dp[q[t]]-q[t]) t--;
        q[++t]=i-mid;
        while(h<=t&&i-mx[i]>q[h]) h++;
        if(h<=t) dp[i]=i+dp[q[h]]-q[h];
        dp[i]=max(dp[i],dp[i-1]);
    }
    if(dp[n]*10>=n*9) return 1;
    return 0;
}
int main()
{
    scanf("%d%d",&T,&m);
    for(re int i=1;i<=m;i++)
    {
        scanf("%s",S+1); int L=strlen(S+1);
        for(re int j=1;j<=L;j++) ins(S[j]-'0');
        ins(2);
    }
    while(T--)
    {
        scanf("%s",S+1),n=strlen(S+1);
        query();
        int l=1,r=n,ans=0;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(check(mid)) ans=mid,l=mid+1;else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}