bzoj 1396: 識別子串
阿新 • • 發佈:2019-01-06
\(SAM\)+線段樹
我竟然還會寫線段樹真是感人至深
考慮到我們只能計算出現一次的子串,所以我們直接先求一個子樹和,只對\(|endpos|=1\)的點操作就好了
我們在\(SAM\)插入的時候可以先存存好每一個字首的結尾位置在哪裡,之後我們對於每一個字首討論其出現次數為\(1\)的字尾
顯然在\(parent\)樹上深度越小的節點的\(len\)就越小,\(|endpos|\)也就越大,於是我們直接從每個字首的結尾節點往上跳,跳到深度最小的\(|endpos|=1\)的節點
這個可以通過樹上倍增做到,不過好像直接暴力看起來更有保障一些
設\(x\)就是我們跳到的點,那麼非常顯然從\(i-len[fa[x]]+1\)
所以對於\([\ 1,i-len[fa[x]]\ ]\)這個區間裡的位置我們都可以標記出一個其能往後延伸的最近位置,顯然就是\(i\),而我們如果倒著迴圈的話,我們就可以保證\(i\)單減,所以我們甚至可以只用一個線段樹來通過區間覆蓋進行更新
而對於\([i-len[fa[x]]+1,i]\)這個區間的位置,我們發現其長度都是固定的,為\(len[fa[x]]+1\),但是我們好像不太能保證\(len[fa[x]]+1\)單調
沒有關係啊,我們離線下來排序就好了
程式碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define maxn 200005 #define re register #define INF 999999999 #define min(a,b) ((a)<(b)?(a):(b)) int num,n,lst=1,cnt=1; char S[maxn>>1]; int to[maxn>>1]; struct E{int v,nxt;} e[maxn]; struct C {int x,y,v;} a[maxn]; int fa[maxn],son[maxn][26],sz[maxn],len[maxn],head[maxn],f[maxn][19],deep[maxn],log_2[maxn]; inline int cmp(C A,C B) {return A.v>B.v;} inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;} void dfs(int x) {for(re int i=head[x];i;i=e[i].nxt) deep[e[i].v]=deep[x]+1,dfs(e[i].v),sz[x]+=sz[e[i].v];} inline void ins(int o,int c) { int f=lst,p=++cnt; lst=p; len[p]=len[f]+1,sz[p]=1; to[o]=p; while(f&&!son[f][c]) son[f][c]=p,f=fa[f]; if(!f) {fa[p]=1;return;} int x=son[f][c]; if(len[f]+1==len[x]) {fa[p]=x;return;} int y=++cnt; len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y; for(re int i=0;i<26;i++) son[y][i]=son[x][i]; while(f&&son[f][c]==x) son[f][c]=y,f=fa[f]; } inline int find(int x) { int k=log_2[deep[x]]+1; for(re int i=k;i>=0;--i) if(f[x][i]&&sz[f[x][i]]<=1) x=f[x][i]; return x; } int l[maxn<<1],r[maxn<<1],tag[maxn<<1],d[maxn>>1],ans[maxn>>1],Ans[maxn>>1]; void build(int x,int y,int i) { l[i]=x,r[i]=y;tag[i]=INF; if(x==y) return; int mid=x+y>>1; build(x,mid,i<<1),build(mid+1,y,i<<1|1); } inline void pushdown(int i) {if(tag[i]==INF) return;tag[i<<1|1]=tag[i<<1]=tag[i];tag[i]=INF;} void change(int x,int y,int val,int i) { if(x<=l[i]&&y>=r[i]) {tag[i]=val;return;} pushdown(i); int mid=l[i]+r[i]>>1; if(y<=mid) change(x,y,val,i<<1); else if(x>mid) change(x,y,val,i<<1|1); else change(x,y,val,i<<1),change(x,y,val,i<<1|1); } int ask(int pos,int i) { if(l[i]==pos&&pos==r[i]) return tag[i]; pushdown(i); int mid=l[i]+r[i]>>1; if(pos<=mid) return ask(pos,i<<1); return ask(pos,i<<1|1); } int main() { scanf("%s",S+1),n=strlen(S+1); for(re int i=1;i<=n;i++) ins(i,S[i]-'a'); for(re int i=2;i<=cnt;i++) add(fa[i],i); deep[1]=1,dfs(1); for(re int i=2;i<=cnt;i++) log_2[i]=log_2[i>>1]+1; for(re int i=1;i<=cnt;i++) f[i][0]=fa[i]; for(re int j=1;j<=log_2[cnt];j++) for(re int i=1;i<=cnt;i++) f[i][j]=f[f[i][j-1]][j-1]; build(1,n,1); for(re int i=n;i;i--) { int x=find(to[i]); if(sz[x]>1) continue; int j=i-len[fa[x]]; change(1,j,i,1); a[n-i+1].x=j+1,a[n-i+1].y=i,a[n-i+1].v=i-j+1; } for(re int i=1;i<=n;i++) d[i]=ask(i,1); std::sort(a+1,a+n+1,cmp); build(1,n,1); for(re int i=1;i<=n;i++) if(a[i].x<=a[i].y) change(a[i].x,a[i].y,a[i].v,1); for(re int i=1;i<=n;i++) ans[i]=ask(i,1),ans[i]=min(ans[i],d[i]-i+1); for(re int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }