【BZOJ1396】识别子串&【BZOJ2865】字符串识别(后缀自动机)

39 篇文章 0 订阅
22 篇文章 0 订阅

题面

自从有了DBZOJ
终于有地方交权限题了

题解

很明显,只出现了一次的串
SAM right/endpos 集合大小一定为 1
换句话说,在parent树上是叶子节点
找到所有这样的节点,
假设它的 len=r ,它父亲的 len=p ,它的结束位置为显然就是 r
l=rp
r 结尾,
并且只出现了一次的串的左端点
1..l,那么,他们的答案可以更新为 r+1i
剩下的位置 l+1..r ,他们无法作为左端点,只能包含在这些串中
于是找到一个最短的包含他们的串 S[l..r]
所以,这段区间的答案可以更新为 rl+1

显然不好一起维护,
于是开两棵线段树,一个维护 r+1i ,先不考虑 i ,最后减去就好
另一个直接维护rl+1

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define lson (now<<1)
#define rson (now<<1|1)
#define MAX 111111
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n;
bool vis[MAX<<1];
struct SAM
{
    struct Node
    {
        int son[26];
        int ff,len;
    }t[MAX<<1];
    int last,tot;
    void init(){last=tot=1;}
    void extend(int c)
    {
        int p=last,np=++tot;last=np;
        t[np].len=t[p].len+1;
        while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
        if(!p)t[np].ff=1;
        else
        {
            int q=t[p].son[c];
            if(t[q].len==t[p].len+1)t[np].ff=q;
            else
            {
                int nq=++tot;
                t[nq]=t[q];t[nq].len=t[p].len+1;
                t[np].ff=t[q].ff=nq;
                while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
            }
        }
    }
}SAM;
char ch[MAX];
struct SegMentTree
{
    struct Node{int v;}t[MAX<<2];
    void Build(int now,int l,int r)
    {
        t[now].v=1e9;if(l==r)return;
        int mid=(l+r)>>1;
        Build(lson,l,mid);Build(rson,mid+1,r);
    }
    void puttag(int now,int w){t[now].v=min(t[now].v,w);}
    void pushdown(int now)
    {
        if(t[now].v==1e9)return;
        puttag(lson,t[now].v);puttag(rson,t[now].v);
        t[now].v=1e9;
    }
    void Modify(int now,int l,int r,int L,int R,int w)
    {
        if(L>R)return;
        if(L<=l&&r<=R){puttag(now,w);return;}
        pushdown(now);int mid=(l+r)>>1;
        if(L<=mid)Modify(lson,l,mid,L,R,w);
        if(R>mid)Modify(rson,mid+1,r,L,R,w);
    }
    int Query(int now,int l,int r,int p)
    {
        if(l==r)return t[now].v;
        pushdown(now);int mid=(l+r)>>1;
        if(p<=mid)return Query(lson,l,mid,p);
        else return Query(rson,mid+1,r,p);
    }
}A,B;
int main()
{
    SAM.init();
    scanf("%s",ch+1);n=strlen(ch+1);
    for(int i=1;i<=n;++i)SAM.extend(ch[i]-97);
    for(int i=1;i<=SAM.tot;++i)vis[SAM.t[i].ff]=true;
    A.Build(1,1,n);B.Build(1,1,n);
    for(int i=1;i<=SAM.tot;++i)
        if(!vis[i])
        {
            int l=SAM.t[i].len-SAM.t[SAM.t[i].ff].len,r=SAM.t[i].len;
            A.Modify(1,1,n,l,r,r-l+1);
            B.Modify(1,1,n,1,l-1,r+1);
        }
    for(int i=1;i<=n;++i)
        printf("%d\n",min(A.Query(1,1,n,i),B.Query(1,1,n,i)-i));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值