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 }