#1762. 识别子串(string)

题意

 

内存限制:128 MiB

时间限制:1000 ms

 

$L \leq 100000$

题解
考虑建出parent树,只有叶子结点上所接受的串在原串中出现一次,而其最长接受的串为原串的一段前缀
所以对于其叶子结点 $i$ 上在原串中对应着 $[1,len_i]$~$[len_i-len_{fa_i},len_i]$
所以对于 $[1,len_i-len_{fa_i}]$ 的点,可以把 $len_i$ 作为其右端点,对于 $[len_i-len_{fa_i}+1,len_i]$ 的点,可以把 $len_{fa_i}+1$ 作为包含它的区间长度
所以开两棵线段树,维护最小右端点和最小长度即可

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,lst=1,sz=1,ans[N];
char s[N];bool g[N];
struct SAM{
    int link,len;
    map<int,int>nx;
}a[N];
void build(int x){
    int np=++sz,p=lst;
    a[np].len=a[p].len+1;
    while(p && !a[p].nx.count(x))
        a[p].nx[x]=np,p=a[p].link;
    if (!p) a[np].link=1;
    else{
        int q=a[p].nx[x];
        if (a[q].len==a[p].len+1)
            a[np].link=q;
        else{
            int nq=++sz;
            a[nq].len=a[p].len+1;
            a[nq].link=a[q].link;
            a[nq].nx=a[q].nx;
            a[q].link=a[np].link=nq;
            while(p && a[p].nx[x]==q)
                a[p].nx[x]=nq,p=a[p].link;
        }
    }
    lst=np;
}
struct T{
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
    int in[N*2];
    void build(int k,int l,int r){
        in[k]=1e9;if (l==r) return;
        build(Ls,l,mid);build(Rs,mid+1,r);
    }
    void update(int k,int l,int r,int L,int R,int v){
        if (L>R) return;
        if (L<=l && r<=R){in[k]=min(in[k],v);return;}
        if (mid>=L) update(Ls,l,mid,L,R,v);
        if (mid<R) update(Rs,mid+1,r,L,R,v);
    }
    int query(int k,int l,int r,int x,int v){
        if (l==r) return min(v,in[k]);
        if (mid>=x) return query(Ls,l,mid,x,min(v,in[k]));
        return query(Rs,mid+1,r,x,min(v,in[k]));
    }
}t[2];
int main(){
    scanf("%s",s+1);n=strlen(s+1);
    for (int i=1;i<=n;i++) build(s[i]-'a');
    for (int i=1;i<=sz;i++) g[a[i].link]=1;
    t[0].build(1,1,n);t[1].build(1,1,n);
    for (int l,r,i=1;i<=sz;i++) if (!g[i])
        r=l=a[i].len,l-=a[a[i].link].len,
        t[0].update(1,1,n,1,l-1,r),
        t[1].update(1,1,n,l,r,r-l+1);
    for (int i=1;i<=n;i++)
        printf("%d\n",min(t[0].query(1,1,n,i,1e9)-i+1,t[1].query(1,1,n,i,1e9)));
    return 0;
}

 

posted @ 2019-04-07 20:54  xjqxjq  阅读(136)  评论(0编辑  收藏  举报