洛谷 P4022 [CTSC2012]熟悉的文章 广义后缀自动机+d

题目大意:
有多个主串,每次询问将询问串分成多个连续子串,如果一个子串长度 ≥ L ≥L L且在主串中出现过就是合法的
如果合法的子串总长度 ≥ ≥ 询问串长的 90 % 90\% 90%,这个串就是合法的字符串,求使得询问串成为合法的字符串的最大的 L L L

分析:
我们可以二分答案,显然答案越大可以使用的合法串就越多,而且是包含关系,满足二分性。
我们把这些串建一个广义后缀自动机。
f [ i ] f[i] f[i] i i i个位置最多可以覆盖多少个位置。
显然有
f [ i ] = m a x ( f [ i − 1 ] , f [ j ] + i − j ) j ∈ [ i − m a x c , i − m i d l e n ] f[i]=max(f[i-1],f[j]+i-j) j\in[i-maxc,i-midlen] f[i]=max(f[i1],f[j]+ij)j[imaxc,imidlen]
其中 m a x c maxc maxc是以当前位置结尾的询问串后缀在所有主串中的 l c s lcs lcs
这个可以在后缀自动机上跳 f a i l fail fail
分三种情况讨论,如果当前节点有儿子,直接往儿子走,maxlen+1。
如果当前点没有这个儿子,跳fail到有这个儿子的节点,显然这个节点代表的串都是可以的(因为后缀自动机每个节点代表不止一个串,但是都是后缀关系),那么显然取最长那个,即 m a x l e n = t [ x ] . l e n + 1 maxlen=t[x].len+1 maxlen=t[x].len+1
如果没有找到儿子,直接 m a x l e n = 0 maxlen=0 maxlen=0

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>

const int maxn=3e6+7;

using namespace std;

int n,m,cnt,len,head,tail;
int f[maxn],q[maxn];
char s[maxn];

struct node{
    int len,fail;
    int son[2];
}t[maxn];

struct rec{
    int son[2];
}a[maxn];

void ins()
{
    int now=1;
    len=strlen(s+1);
    for (int i=1;i<=len;i++)
    {
        int c=s[i]-'0';
        if (!a[now].son[c]) a[now].son[c]=++cnt;
        now=a[now].son[c];
    }
}

int ins_sam(int x,int c)
{
    int now,p,q,clone;
    p=x;
    if (t[p].son[c])
    {
        q=t[p].son[c];
        if (t[p].len+1==t[q].len) return q;
        clone=++cnt;
        t[clone]=t[q];
        t[clone].len=t[p].len+1;
        t[q].fail=clone;
        while (p&&(t[p].son[c]==q)) t[p].son[c]=clone,p=t[p].fail;
        return clone;
    }
    now=++cnt;
    t[now].len=t[p].len+1;
    while (p&&(!t[p].son[c])) t[p].son[c]=now,p=t[p].fail;
    if (!p) t[now].fail=1;
    else
    {
        q=t[p].son[c];
        if (t[p].len+1==t[q].len) t[now].fail=q;
        else
        {
            clone=++cnt;
            t[clone]=t[q];
            t[clone].len=t[p].len+1;
            t[now].fail=t[q].fail=clone;
            while (p&&(t[p].son[c]==q)) t[p].son[c]=clone,p=t[p].fail;
        }
    }
    return now;
}

void dfs(int x,int last)
{
    int d;
    if (a[x].son[0])
    {
        d=ins_sam(last,0);
        dfs(a[x].son[0],d);
    }
    if (a[x].son[1])
    {
        d=ins_sam(last,1);
        dfs(a[x].son[1],d);
    }
}

bool check(int k)
{
    int p=1,maxlen=0;
    f[0]=0;
    head=1,tail=0;	
    for (int i=1;i<=len;i++)
    {
        int c=s[i]-'0';
        if (t[p].son[c]) maxlen++,p=t[p].son[c];
        else
        {
            while (p&&(!t[p].son[c])) p=t[p].fail;
            if (!p) p=1,maxlen=0;
            else
            {
                maxlen=t[p].len+1;
                p=t[p].son[c];
            }
        }
        f[i]=f[i-1];
        if (i-k>=0) q[++tail]=i-k;
        while ((head<tail) && (f[q[tail]]-q[tail]>=f[q[tail-1]]-q[tail-1])) q[tail-1]=q[tail],tail--;
        if (maxlen>=k)
        {			
            while (q[head]<i-maxlen) head++;
            if (q[head]<=i-k) f[i]=max(f[i],f[q[head]]+(i-q[head]));
            
        } 
    }
    return f[len]*10>=len*9;
}

int main()
{
    scanf("%d%d",&m,&n);
    cnt=1;	
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        ins();
    }	
    cnt=1;	
    dfs(1,1);
    for (int i=1;i<=m;i++)
    {
        scanf("%s",s+1);	
        len=strlen(s+1);	
        int l=1,r=len,ans=0;
        while (l<=r)
        {
            int mid=(l+r)/2;
            if (check(mid)) l=mid+1,ans=mid;
                       else r=mid-1;
        }
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值