1. 程式人生 > >BZOJ 1396 識別子串 (字尾自動機+線段樹)

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 }