1. 程式人生 > >BZOJ1396&2865 識別子串 【後綴自動機 + 線段樹】

BZOJ1396&2865 識別子串 【後綴自動機 + 線段樹】

分享圖片 題目 != down 長度 namespace ostream 數量 後綴

題目

技術分享圖片

輸入格式

一行,一個由小寫字母組成的字符串S,長度不超過10^5

輸出格式

L行,每行一個整數,第i行的數據表示關於S的第i個元素的最短識別子串有多長.

輸入樣例

agoodcookcooksgoodfood

輸出樣例

1

2

3

3

2

2

3

3

2

2

3

3

2

1

2

3

3

2

1

2

3

4

題解

BZOJ AC200紀念,,

這兩題題幹是一樣的,但唯一不同的是。。後者卡空間【MLE得飛起】
先說解法:
我們知道後綴自動機上的parent樹的每個節點子樹中葉子的數量就是該節點表示的串的出現次數
顯然我們要找的是子樹葉子數為1,即代表出現次數為1的節點

parent樹中的節點,要麽是葉子節點,要麽有至少兩個兒子

由上面這個性質我們可以知道滿足條件的只有葉子結點
所以我們連序都不用排了,直接統計不被父親指針指向的節點

對於節點i,其表示的串為長度為\([step[pre[i]] + 1,step[i]]\)的串【pre為parent樹父親節點】
由於該串是唯一的,所以該串表示的最小串為後綴的子串都是唯一的
我們令其最小串為[l,r],其中\(l = step[i] - step[pre[i]],r = step[i]\)
我們分為兩類:
①被最小串包含的位置[l,r],更新其最短識別長度為\(r - l + 1\)
②超出最小串的位置[1,l-1],更新其最短識別長度為\(r - i + 1\)【i為字符位置】

那麽我們可以開兩顆線段樹分別維護①和②的最小值,最後輸出時二者取最小即可【維護②的先不減i,輸出時再減】
什麽意思?每個位置會被兩種形式的長度更新,①是一個值,②是一個值還要減去自身的位置,②不好處理,我們分開保存

T1就搞完了~~

等等,T2呢?
原來是數據範圍變大了,改大,一交,霧草。。MLE
= =
何破?
①改用後綴數組【還沒寫】
②將ch改為map保存【好吧我就是這樣A的】

T2太丟臉,貼T1代碼吧,,

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm> #define LL long long int #define REP(i,n) for (int i = 1; i <= (n); i++) #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts(""); #define ls (u << 1) #define rs (u << 1 | 1) using namespace std; const int maxn = 200005,maxm = 400005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57) {if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - ‘0‘; c = getchar();} return out * flag; } int pre[maxn],ch[maxn][26],step[maxn],val[maxn],cnt,last,n; char s[maxn]; void ins(int x){ int p = last,np = ++cnt; last = np; step[np] = step[p] + 1; while (p && !ch[p][x]) ch[p][x] = np,p = pre[p]; if (!p) pre[np] = 1; else { int q = ch[p][x]; if (step[q] == step[p] + 1) pre[np] = q; else { int nq = ++cnt; step[nq] = step[p] + 1; memcpy(ch[nq],ch[q],sizeof(ch[q])); pre[nq] = pre[q]; pre[q] = pre[np] = nq; while (ch[p][x] == q) ch[p][x] = nq,p = pre[p]; } } } struct Seg{ int mn[2 * maxn],tag[2 * maxn]; Seg(){for (int i = 0; i < maxm; i++) mn[i] = tag[i] = INF;} void pd(int u){ if (tag[u] != INF){ mn[ls] = min(mn[ls],tag[u]); tag[ls] = min(tag[ls],tag[u]); mn[rs] = min(mn[rs],tag[u]); tag[rs] = min(tag[rs],tag[u]); tag[u] = INF; } } void modify(int u,int l,int r,int L,int R,int v){ if (L > R) return; if (l >= L && r <= R){ mn[u] = min(mn[u],v); tag[u] = min(tag[u],v); return; } pd(u); int mid = l + r >> 1; if (mid >= L) modify(ls,l,mid,L,R,v); if (mid < R) modify(rs,mid + 1,r,L,R,v); mn[u] = min(mn[ls],mn[rs]); } int query(int u,int l,int r,int pos){ if (l == r) return mn[u]; pd(u); int mid = l + r >> 1; if (mid >= pos) return query(ls,l,mid,pos); else return query(rs,mid + 1,r,pos); } }A,B; void solve(){ REP(i,cnt) val[i] = 1; REP(i,cnt) val[pre[i]] = 0; REP(i,cnt) if (val[i]){ int l = step[i] - step[pre[i]],r = step[i]; A.modify(1,1,n,l,r,r - l + 1); B.modify(1,1,n,1,l - 1,r + 1); } REP(i,n) printf("%d\n",min(A.query(1,1,n,i),B.query(1,1,n,i) - i)); } int main(){ //freopen("in.txt","r",stdin); //freopen("out1.txt","w",stdout); scanf("%s",s + 1); n = strlen(s + 1); cnt = last = 1; REP(i,n) ins(s[i] - ‘a‘); solve(); return 0; }

BZOJ1396&2865 識別子串 【後綴自動機 + 線段樹】