bzoj千題計劃318:bzoj1396: 識別子串(後綴自動機 + 線段樹)

https://www.lydsy.com/JudgeOnline/problem.php?id=1396php

 

後綴自動機的parent樹上,若是不是葉子節點,那麼至少有兩個子節點ui

而一個狀態所表明子串的出現次數就是子樹中葉子節點的個數spa

因此只有葉子節點 即 |Right|=1的狀態 表明的子串 出現了1次code

咱們計算以每個位置爲子串右端點時,它對一些位置的貢獻blog

枚舉|Right|=1的狀態sget

令end=Right(s)string

那麼以end爲子串右端點,長度在[1,Max(parent(s))]的子串至少還會在s的父節點表示的狀態中出現it

因此在以end爲識別子串右端點時io

位置[end-Max(parent(s)),end]的最短長度爲Max(parent(s)+1ast

位置[end-Max(s)+1,end-Max(parent(s))]的最短長度爲 end-i+1

用兩棵線段樹維護

一棵直接維護最小值

另外一棵維護end+1的最小值,查詢的時候將結果-i

 

#include<cstdio>
#include<cstring>
#include<algorithm>
 
#define N 100001
 
using namespace std;
 
char s[N];
 
int ch[N<<1][26],tot=1;
int fa[N<<1],len[N<<1];
int siz[N<<1]; 
int last=1,p,q,np,nq;
 
int leaf[N];
 
int v[N],sa[N<<1];
 
struct Segment
{
    int mx[N<<2];
    int tag[N<<2];
 
    void down(int k)
    {
        mx[k<<1]=min(mx[k<<1],tag[k]);
        mx[k<<1|1]=min(mx[k<<1|1],tag[k]);
        tag[k<<1]=min(tag[k<<1],tag[k]);
        tag[k<<1|1]=min(tag[k<<1|1],tag[k]);
        tag[k]=2e9;
    }
 
    void build(int k,int l,int r)
    {
        mx[k]=tag[k]=2e9;
        if(l==r) return;
        int mid=l+r>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
    }
 
    void change(int k,int l,int r,int opl,int opr,int w)
    {
        if(l>=opl && r<=opr)
        {
            mx[k]=min(mx[k],w);
            tag[k]=min(tag[k],w);
            return;
        }
        int mid=l+r>>1;
        if(tag[k]!=2e9) down(k);
        if(opl<=mid) change(k<<1,l,mid,opl,opr,w);
        if(opr>mid) change(k<<1|1,mid+1,r,opl,opr,w);
        mx[k]=min(mx[k<<1],mx[k<<1|1]);
    }
 
    int query(int k,int l,int r,int x)
    {
        if(l==r) return mx[k];
        int mid=l+r>>1;
        if(tag[k]!=2e9) down(k);
        if(x<=mid) return query(k<<1,l,mid,x);
        return query(k<<1|1,mid+1,r,x);
    }
};
 
Segment tr1,tr2;
 
void extend(int c)
{
    len[np=++tot]=len[last]+1;
    siz[tot]=1;
    for(p=last;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
    if(!p) fa[np]=1;
    else
    {
        q=ch[p][c];
        if(len[q]==len[p]+1) fa[np]=q;
        else
        {
            nq=++tot;
            fa[nq]=fa[q];
            memcpy(ch[nq],ch[q],sizeof(ch[nq]));
            fa[q]=fa[np]=nq;
            len[nq]=len[p]+1;
            for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
        }
    }
    last=np;
}
     
int main()
{
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=1;i<=n;++i) 
    {
        leaf[i]=tot+1;
        extend(s[i]-'a');
    }
    for(int i=1;i<=tot;++i) v[len[i]]++;
    for(int i=1;i<=n;++i) v[i]+=v[i-1];
    for(int i=1;i<=tot;++i) sa[v[len[i]]--]=i;
    int x;
    for(int i=tot;i;--i)
    {
        x=sa[i];
        siz[fa[x]]+=siz[x];
    }
    tr1.build(1,1,n);
    tr2.build(1,1,n);
    int l,r,end;
    for(int x=2;x<=tot;++x)
    if(siz[x]==1)
    {
        l=len[fa[x]];
        r=len[x];
        end=len[x];
        tr1.change(1,1,n,end-l,end,l+1);
        tr2.change(1,1,n,end-r+1,end-l,end+1);
    }
    int a,b;
    for(int i=1;i<=n;++i)
    {
        a=tr1.query(1,1,n,i);
        b=tr2.query(1,1,n,i)-i;
        printf("%d\n",min(a,b));
    }
    return 0;
}
相關文章
相關標籤/搜索