BZOJ1396 识别子串 和 BZOJ2865 字符串识别

题意

Problem 2865. -- 字符串识别

2865: 字符串识别

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 839   Solved: 261
[ Submit][ Status][ Discuss]

Description

XX在进行字符串研究的时候,遇到了一个十分棘手的问题。

在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件:

1、iKj

2、子串T只在S中出现过一次。

例如,S="banana"K=5,则关于第K位的识别子串有"nana""anan""anana""nan""banan""banana"

现在,给定SXX希望知道对于S的每一位,最短的识别子串长度是多少,请你来帮助他。

Input

仅一行,输入长度为N的字符串S

Output

输出N行,每行一个整数,第i行的整数表示对于第i位的最短识别子串长度。

Sample Input

agoodcookcooksgoodfood

Sample Output


1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4

HINT

N<=5*10^5

Source

[ Submit][ Status][ Discuss]

HOME Back

分析

参照jklover的题解。

SAM + 线段树.

先建出 parent 树,按照题意,我们只需要处理 right 集合大小为 1 的节点.
如下图,先算出这样的一个节点合法长度的 max,min ( min 可以用 max(fa)+1 计算).


那么区域 I 内每个点的贡献就是区域 II 的长度加上这个点到区域 II 的距离.
区域 II 内每个点的贡献就是区间 II 的长度.开两颗线段树分别修改就可以了.

如何维护区域1呢?考虑他的答案是pos[u]-i+1,直接更新pos[u],那么把最终线段树上的值-i就是答案了。这种操作比较常见。

时间复杂度\(O(n \log n)\)

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
    while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int N=2e5,INF=0x3f3f3f3f;
struct node{int min,tag;};
#define lc (x<<1)
#define rc (x<<1|1)
struct SegTree{
    node t[N*4];
    void pushup(int x){
        t[x].min=min(t[lc].min,t[rc].min);
    }
    void modify(int x,int v){
        t[x].min=min(t[x].min,v),t[x].tag=min(t[x].tag,v);
    }
    void pushdown(int x){
        if(t[x].tag<INF){
            modify(lc,t[x].tag),modify(rc,t[x].tag);
            t[x].tag=INF;
        }
    }
    void build(int x,int l,int r){
        t[x].min=t[x].tag=INF;
        if(l==r) return;
        int mid=l+r>>1;
        build(lc,l,mid),build(rc,mid+1,r);
    }
    void update(int x,int l,int r,int ql,int qr,int v){
        if(ql<=l&&r<=qr) return modify(x,v);
        pushdown(x);
        int mid=l+r>>1;
        if(ql<=mid) update(lc,l,mid,ql,qr,v);
        if(qr>mid) update(rc,mid+1,r,ql,qr,v);
        pushup(x);
    }
}T1,T2;
int ans[N];
void query(int x,int l,int r){
    if(l==r) return ans[l]=min(T1.t[x].min-l,T2.t[x].min),void();
    T1.pushdown(x),T2.pushdown(x);
    int mid=l+r>>1;
    query(lc,l,mid),query(rc,mid+1,r);
}
// SAM
char buf[N];
int n,tot=1,last=1;
int ch[N][26],fa[N],len[N],pos[N],siz[N];
void extend(int c,int po){
    int p=last,cur=last=++tot;
    len[cur]=len[p]+1,pos[cur]=po,siz[cur]=1;
    for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
    if(!p) fa[cur]=1;
    else{
        int q=ch[p][c];
        if(len[q]==len[p]+1) fa[cur]=q;
        else{
            int clone=++tot;
            memcpy(ch[clone],ch[q],sizeof ch[q]);
            fa[clone]=fa[q],len[clone]=len[p]+1,pos[clone]=pos[q];
            fa[q]=fa[cur]=clone;
            for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
        }
    }
}
int cnt[N],ord[N];
int main(){
    scanf("%s",buf+1),n=strlen(buf+1);
    for(int i=1;i<=n;++i) extend(buf[i]-'a',i);
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
    for(int i=1;i<=tot;++i) ord[cnt[len[i]]--]=i;
    T1.build(1,1,n),T2.build(1,1,n);
    for(int i=tot;i>=1;--i){
        int u=ord[i];
        siz[fa[u]]+=siz[u];
        if(u==1||siz[u]>1) continue;
        int l=pos[u]-len[u]+1,r=pos[u]-len[fa[u]];
        if(l<=r-1) T1.update(1,1,n,l,r-1,pos[u]+1);
        T2.update(1,1,n,r,pos[u],len[fa[u]]+1);
    }
    query(1,1,n);
    for(int i=1;i<=n;++i) printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/10805402.html