[CTSC2012]熟悉的文章

一、题目

点此看题

二、解法

显然 L L 满足单调性,可以二分。

考虑检查,可以 d p dp ,设 d p [ i ] dp[i] 为分段到 i i ,最多能够匹配的长度,转移如下:
d p [ i ] = { d p [ i 1 ] d p [ j ] + i j ( i m t [ i ] j i L ) dp[i]=\begin{cases}dp[i-1]\\dp[j]+i-j&(i-mt[i]\leq j\leq i-L)\end{cases} 其中 m t [ i ] mt[i] 是指前缀 i i 最多能够匹配的长度,这个就需要后缀自动机,我们把每一个标准作文串丢进广义后缀自动机中,然后把待检查的作文拿去匹配,如果能够转移就在当前长度的基础上加一,否则跳 p a r e n t parent 树,看祖先能否转移,在第一个能转移的祖先处停止,修改长度,如果没有祖先有转移,就把匹配点赋值为 1 1 ,长度赋值为 0 0 m t [ i ] mt[i] 就是匹配到 i i 时的长度。

然后在二分里面 d p dp ,时间复杂度 O ( n 2 ) O(n^2) 。但是这个 d p dp 其实可以单调队列优化, i i 每次增加 1 1 m t mt 最多每次加 1 1 ,所以 i m t [ i ] i-mt[i] 单调递增,从 L L 开始 d p dp ,每次把 i L i-L d p dp 值插入单调队列中,然后时间复杂度就可以优化到 O ( n ) O(n)

时间复杂度 O ( n log n ) O(n\log n) ,更多细节可以参考我的代码。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 2000005;
int read()
{
 int x=0,flag=1;char c;
 while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
 while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
 return x*flag;
}
int n,m,k;char s[M];
struct node
{
    int len,fa,ch[2];
    node() {memset(ch,0,sizeof ch);len=fa=0;}
};
struct automaton
{
    int cnt,last,ans,dp[M],q[M],mt[M];node a[M];
    automaton() {cnt=last=1;}
    void add(int c)
    {
        int p=last,np=last=++cnt;
        a[np].len=a[p].len+1;
        for(;p && !a[p].ch[c];p=a[p].fa) a[p].ch[c]=np;
        if(!p) a[np].fa=1;
        else
        {
            int q=a[p].ch[c];
            if(a[q].len==a[p].len+1) a[np].fa=q;
            else
            {
                int nq=++cnt;
                a[nq]=a[q];a[nq].len=a[p].len+1;
                a[q].fa=a[np].fa=nq;
                for(;p && a[p].ch[c]==q;p=a[p].fa) a[p].ch[c]=nq;
            }
        }
    }
    void match()
    {
        int p=1,len=0,c=0;
        for(int i=1;i<=n;i++)
        {
            c=s[i]-'0';
            if(a[p].ch[c])
                p=a[p].ch[c],len++;
            else
            {
                for(;p && !a[p].ch[c];p=a[p].fa);
                if(p) len=a[p].len+1,p=a[p].ch[c];
                else len=0,p=1;
            }
            mt[i]=len;
        }
    }
    bool check(int L)
    {
        for(int i=0;i<L;i++) dp[i]=0;
        int head=1,tail=0;
        for(int i=L;i<=n;i++)
        {
            while(head<=tail && dp[q[tail]]-q[tail]<=dp[i-L]-(i-L)) tail--;
            q[++tail]=i-L;
            while(head<=tail && q[head]<i-mt[i]) head++;
            dp[i]=dp[i-1];
            if(head<=tail) dp[i]=max(dp[i],dp[q[head]]+i-q[head]);
        }
        return dp[n]*10>=n*9;
    }
    void work(int l,int r)
    {
        if(l>r) return ;
        int mid=(l+r)>>1;
        if(check(mid))
        {
            ans=mid;
            work(mid+1,r);
        }
        else work(l,mid-1);
    }
    void solve()
    {
        ans=0;
        scanf("%s",s+1);
        n=strlen(s+1);
        match();
        work(1,n);
        printf("%d\n",ans);
    }
}Sam;
int main()
{
    m=read();k=read();
    for(int i=1;i<=k;i++)
    {
        Sam.last=1;//
        scanf("%s",s);
        n=strlen(s);
        for(int j=0;j<n;j++)
            Sam.add(s[j]-'0');
    }
    for(int i=1;i<=m;i++)
        Sam.solve();
}
发布了257 篇原创文章 · 获赞 13 · 访问量 6730

猜你喜欢

转载自blog.csdn.net/C202044zxy/article/details/104166285