2018.12.23-bzoj-1396: 识别子串

题目描述:

算法标签:后缀自动机

思路:

一个串在后缀自动机中sz为1,即可对答案做出贡献,我们考虑可以造成哪几种贡献:

minnlen=d[fa[x]]+1,maxnlen=d[x].

1.对于区间[endpos-maxnlen,endpos-minnlen],贡献是endpos+1-i.

2.对于区间[endpos-minnlen,endpos],贡献是minnlen.

用两棵线段树统计答案即可。

以下代码:

#include<bits/stdc++.h>
#define il inline
using namespace std;
const int N=2e5+5;
char s[N];int n,cnt=1,cur=1,last,ch[N][26],d[N],fa[N],mn[N<<1],a[N],c[N],sz[N],r[N],b[N<<1],end[N];
il void build(int c,int id){
    last=cur;cur=++cnt;d[cur]=id;
    int p=last;end[cur]=id;
    for(;p&&!ch[p][c];p=fa[p])ch[p][c]=cur;
    if(!p)fa[cur]=1;
    else{
        int q=ch[p][c];
        if(d[q]==d[p]+1)fa[cur]=q;
        else{
            int nt=++cnt;d[nt]=d[p]+1;
            memcpy(ch[nt],ch[q],sizeof(ch[q]));
            fa[nt]=fa[q];fa[q]=fa[cur]=nt;
            for(;ch[p][c]==q;p=fa[p])ch[p][c]=nt;
        }
    }
    sz[cur]=1;
}
il void init(int x,int l,int r){
    mn[x]=N;b[x]=N;if(l==r)return;int mid=(l+r)>>1;
    init(x<<1,l,mid);init(x<<1|1,mid+1,r);
}
il void C(int x,int l,int r,int ql,int qr,int v){
    if(ql<=l&&r<=qr){mn[x]=min(mn[x],v);return;}
    int mid=(l+r)>>1;
    if(ql<=mid)C(x<<1,l,mid,ql,qr,v);
    if(mid<qr)C(x<<1|1,mid+1,r,ql,qr,v);
}
il void C2(int x,int l,int r,int ql,int qr,int v){
    if(ql<=l&&r<=qr){b[x]=min(b[x],v);return;}
    int mid=(l+r)>>1;
    if(ql<=mid)C2(x<<1,l,mid,ql,qr,v);
    if(mid<qr)C2(x<<1|1,mid+1,r,ql,qr,v);
}
il int q1(int x,int l,int r,int pos){
    if(l==r)return mn[x];int mid=(l+r)>>1;
    if(pos<=mid)return min(q1(x<<1,l,mid,pos),mn[x]);
    else return min(q1(x<<1|1,mid+1,r,pos),mn[x]);
}
il int q2(int x,int l,int r,int pos){
    if(l==r)return b[x];int mid=(l+r)>>1;
    if(pos<=mid)return min(q2(x<<1,l,mid,pos),b[x]);
    else return min(q2(x<<1|1,mid+1,r,pos),b[x]);
}
int main()
{
    scanf(" %s",s+1);n=strlen(s+1);
    for(int i=1;i<=n;i++)build(s[i]-'a',i);
    for(int i=1;i<=cnt;i++)c[d[i]]++;
    for(int i=1;i<=n;i++)c[i]+=c[i-1];
    for(int i=cnt;i;i--)a[c[d[i]]--]=i;init(1,1,n);
    for(int i=cnt;i;i--){
        sz[fa[a[i]]]+=sz[a[i]];end[fa[a[i]]]=end[a[i]];
        if(sz[a[i]]==1){
            int mnl=d[fa[a[i]]]+1,mnr=d[a[i]];
            C(1,1,n,end[a[i]]-mnr+1,end[a[i]]-mnl+1,end[a[i]]+1);
            C2(1,1,n,end[a[i]]-mnl+1,end[a[i]],mnl);
        }
    }
    for(int i=1;i<=n;i++){
        int res=q1(1,1,n,i)-i;
        printf("%d
",min(res,q2(1,1,n,i)));
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Jessie-/p/10166714.html