BZOJ 1396 识别子串 (后缀自动机+线段树)

题目大意:

给你一个字符串S,求关于每个位置x的识别串T的最短长度,T必须满足覆盖x,且T在S中仅出现一次

神题

以节点x为结尾的识别串,必须满足它在$parent$树的子树中只有一个$endpos$节点

若令$fa=pre_{x},L=dep_{fa},R=dep_{x}$

1.对于以$i\in[1,R-L]$为开头,以$R$为结尾的串,合法串的长度是$R-i+1$

如果在$S_{1...x}$串中不断删去开头的字符,删到串剩余长度为$dep_{fa}$时结束

上述过程在$parent$树里的表现为,从表示串$S_{1...x}$的节点$a$,删掉最后一个字符后,跳到了表示$S_{x-dep_{fa}+1...x}$的节点$b$

不必考虑$b$的$right$集合大小,因为$R$是递增的,如果$b$节点的串能作为识别串,$a$节点的答案一定不如$b$优秀

所以我们干脆只讨论不跳到$b$的情况就行了

那么$i\in[1,R-L]$为开头,$R$为结尾的的串一定都能作为$[1,R-L]$的识别串

2.对于以$i\in[R-L+1,R]$为开头,以$R$为结尾的串,合法串的长度是$R-L+1$

一样的道理,如果从$a$跳到了$b$,$b$节点的串可能不合法,但$a$节点的串一定合法

那么以$L$为开头,$R$为结尾的的串一定都能作为$[1,R-L+1]$的识别串

区间修改,单点查询,开两颗线段树维护一下就好

  1 #include <vector>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define N1 105000
  6 #define S1 (N1<<1)
  7 #define T1 (N1<<2)
  8 #define ll long long
  9 #define uint unsigned int
 10 #define rint register int 
 11 #define il inline 
 12 #define inf 0x3f3f3f3f
 13 #define idx(X) (X-'a')
 14 using namespace std;
 15 
 16 int gint()
 17 {
 18     int ret=0,fh=1;char c=getchar();
 19     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
 20     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
 21     return ret*fh;
 22 }
 23 char str[N1];
 24 int len;
 25 struct Seg{
 26 int mi[S1<<2];
 27 void pushdown(int rt){
 28     mi[rt<<1]=min(mi[rt<<1],mi[rt]);
 29     mi[rt<<1|1]=min(mi[rt<<1|1],mi[rt]);}
 30 void build(int l,int r,int rt)
 31 {
 32     mi[rt]=inf;
 33     if(l==r) return;
 34     int mid=(l+r)>>1;
 35     build(l,mid,rt<<1);
 36     build(mid+1,r,rt<<1|1);
 37 }
 38 void update(int L,int R,int l,int r,int rt,int w)
 39 {
 40     if(L<=l&&r<=R) {mi[rt]=min(w,mi[rt]);return;}
 41     pushdown(rt);int mid=(l+r)>>1;
 42     if(L<=mid) update(L,R,l,mid,rt<<1,w);
 43     if(R>mid) update(L,R,mid+1,r,rt<<1|1,w);
 44 }
 45 int query(int x,int l,int r,int rt)
 46 {
 47     if(l==r) return mi[rt];
 48     pushdown(rt);int mid=(l+r)>>1;
 49     if(x<=mid) return query(x,l,mid,rt<<1);
 50     else return query(x,mid+1,r,rt<<1|1);
 51 }
 52 }s1,s2;
 53 namespace SAM{
 54 int trs[S1][26],pre[S1],dep[S1],ed[S1],sz[S1],tot,la;
 55 void init(){tot=la=1;}
 56 void reduct(){la=1;}
 57 void insert(int c)
 58 {
 59     int p=la,np=++tot,q,nq;la=np;
 60     dep[np]=dep[p]+1;ed[np]=1;
 61     for(;p&&!trs[p][c];p=pre[p]) trs[p][c]=np;
 62     if(!p) {pre[np]=1;return;}
 63     q=trs[p][c];
 64     if(dep[q]==dep[p]+1) pre[np]=q;
 65     else{
 66         pre[nq=++tot]=pre[q];
 67         pre[q]=pre[np]=nq;
 68         dep[nq]=dep[p]+1;
 69         memcpy(trs[nq],trs[q],sizeof(trs[q]));
 70         for(;p&&trs[p][c]==q;p=pre[p]) trs[p][c]=nq;
 71     }
 72 }
 73 int hs[S1],que[S1],ans[N1];
 74 void solve()
 75 {
 76     for(int i=2;i<=tot;i++) hs[dep[i]]++;
 77     for(int i=1;i<=len;i++) hs[i]+=hs[i-1];
 78     for(int i=2;i<=tot;i++) que[hs[dep[i]]--]=i;
 79     int x,fx;
 80     for(int i=tot-1;i>0;i--)
 81     {
 82         x=que[i];
 83         sz[x]+=(ed[x]?1:0);
 84         sz[pre[x]]+=sz[x];
 85     }
 86     s1.build(1,tot,1);
 87     s2.build(1,tot,1);
 88     for(int i=2;i<=tot;i++)
 89     {
 90         x=que[i],fx=pre[x];
 91         if(sz[x]>1) continue;
 92         if(dep[fx]>=1) s1.update(dep[x]-dep[fx]+1,dep[x],1,tot,1,dep[fx]+1);
 93         if(dep[fx]+1<=dep[x]) s2.update(1,dep[x]-dep[fx],1,tot,1,dep[x]);
 94     }
 95     for(int i=1;i<=len;i++)
 96     {
 97         ans[i]=min(s1.query(i,1,tot,1),s2.query(i,1,tot,1)-i+1);
 98         printf("%d\n",ans[i]);
 99     }
100 }
101 };
102 
103 int main()
104 {
105     //freopen("t2.in","r",stdin);
106     scanf("%s",str+1);
107     len=strlen(str+1);
108     SAM::init();
109     for(int i=1;i<=len;i++)
110         SAM::insert(idx(str[i]));
111     SAM::solve();
112     return 0;
113 }

 

posted @ 2018-12-11 18:25  guapisolo  阅读(182)  评论(0编辑  收藏  举报