【BZOJ1396】识别子串 - 后缀自动机+线段树
题意:
Description
Input
一行,一个由小写字母组成的字符串S,长度不超过10^5
Output
L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长.
题解:
先建出SAM,显然right集合大小为1的子串,即在parent树上的叶子节点可以作为识别子串;
考虑一个这样的子串会对哪些区间产生影响:
设$l=max[fa[s]]$,$r=max[s]$,显然这个子串出现的位置就是$r$,所以对区间$[1,r]$都有影响;
但是其中有一段是被$fa[s]$包含的,因此贡献不同,具体来说就是:
在区间$[1,l-1]$中贡献为$r-i+1$;
在区间$[l,r]$中贡献为$r-l+1$;
这里手推一下就好;
因此开两棵线段树维护修改最小值即可。
代码:
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #include<queue>
7 #include<set>
8 #define inf 0x7f7f7f7f
9 #define eps 1e-9
10 #define mp make_pair
11 using namespace std;
12 typedef long long ll;
13 typedef double db;
14 int n,l,r,last=1,rt=1,tot=1,son[200001][26],fa[200001],mx[200001];
15 bool ch[200001];
16 char s[100001];
17 struct seg{
18 int t[2000001];
19 seg(){
20 memset(t,0x7f,sizeof(t));
21 }
22 void pd(int u){
23 if(t[u]!=inf){
24 t[u*2]=min(t[u*2],t[u]);
25 t[u*2+1]=min(t[u*2+1],t[u]);
26 t[u]=inf;
27 }
28 }
29 void updata(int l,int r,int u,int L,int R,int x){
30 if(L>R)return;
31 if(L<=l&&r<=R){
32 t[u]=min(t[u],x);
33 return;
34 }
35 pd(u);
36 int mid=(l+r)/2;
37 if(L<=mid)updata(l,mid,u*2,L,R,x);
38 if(mid<R)updata(mid+1,r,u*2+1,L,R,x);
39 }
40 int query(int l,int r,int u,int p){
41 if(l==r)return t[u];
42 pd(u);
43 int mid=(l+r)/2;
44 if(p<=mid)return query(l,mid,u*2,p);
45 else return query(mid+1,r,u*2+1,p);
46 }
47 }t1,t2;
48 void extend(int ch){
49 int p=last,np=++tot;
50 mx[np]=mx[p]+1;
51 for(;p&&!son[p][ch];p=fa[p])son[p][ch]=np;
52 if(!p)fa[np]=rt;
53 else{
54 int q=son[p][ch];
55 if(mx[q]==mx[p]+1)fa[np]=q;
56 else{
57 int nq=++tot;
58 mx[nq]=mx[p]+1;
59 memcpy(son[nq],son[q],sizeof(son[q]));
60 fa[nq]=fa[q];
61 fa[q]=fa[np]=nq;
62 for(;p&&son[p][ch]==q;p=fa[p])son[p][ch]=nq;
63 }
64 }
65 last=np;
66 }
67 int main(){
68 scanf("%s",s);
69 n=strlen(s);
70 for(int i=0;i<n;i++)extend(s[i]-'a');
71 for(int i=1;i<=tot;i++){
72 if(fa[i])ch[fa[i]]=true;
73 }
74 for(int i=1;i<=tot;i++){
75 if(!ch[i]){
76 l=mx[i]-mx[fa[i]],r=mx[i];
77 t1.updata(1,n,1,1,l-1,r+1);
78 t2.updata(1,n,1,l,r,r-l+1);
79 }
80 }
81 for(int i=1;i<=n;i++){
82 printf("%d\n",min(t1.query(1,n,1,i)-i,t2.query(1,n,1,i)));
83 }
84 return 0;
85 }