1. 程式人生 > >CF700E:Cool Slogans(後綴自動機,線段樹合並)

CF700E:Cool Slogans(後綴自動機,線段樹合並)

turn 線段 query root for 自動 ans har ==

Description

給你一個字符串,如果一個串包含兩個不重疊的相同子串,那麽這個串的價值就是子串的價值+1。問你給定字符串的最大價值子串的價值。

Input

第一行讀入字符串長度$n$,第二行是字符串。

Output

一行答案。

Sample Input1

3
abc

Sample Output1

1

Sample Input2

5
ddddd

Sample Output2

5

Sample Input3

11
abracadabra

Sample Output3

3

Solution

首先把後綴樹建立出來,然後從下往上線段樹合並一下$endpos$。

設$f[i]$表示從後綴樹的根$DP$到了$i$節點的最大價值,$top[i]$表示$i$節點是從哪個節點轉移來的。

如果父親代表的字符串在當前節點代表的字符串中出現了兩次及以上,那麽就$f[x]=f[fa]+1,top[x]=x$

否則$f[x]=f[fa],top[x]=top[fa]$

父親代表的字符串在當前節點代表的字符串中出現的次數可以直接根據$SAM$的$step$數組和線段樹合並出的$endpos$什麽的直接判斷一下。

Code

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #define N (800009)
  5 using namespace std;
6 7 struct Sgt{int ls,rs,val;}Segt[N<<5]; 8 struct Edge{int to,next;}edge[N<<1]; 9 int n,ans,sgt_num,Root[N],f[N],top[N]; 10 int head[N],num_edge; 11 int last=1,p,q,np,nq,cnt=1; 12 int fa[N],son[N][26],step[N],pos[N]; 13 char s[N]; 14 15 void add(int u,int v) 16
{ 17 edge[++num_edge].to=v; 18 edge[num_edge].next=head[u]; 19 head[u]=num_edge; 20 } 21 22 void Insert(int x,int r) 23 { 24 p=last; np=last=++cnt; 25 step[np]=step[p]+1; pos[np]=r; 26 while (p && !son[p][x]) son[p][x]=np, p=fa[p]; 27 if (!p) fa[np]=1; 28 else 29 { 30 q=son[p][x]; 31 if (step[q]==step[p]+1) fa[np]=q; 32 else 33 { 34 nq=++cnt; step[nq]=step[p]+1; pos[nq]=r; 35 memcpy(son[nq],son[q],sizeof(son[q])); 36 fa[nq]=fa[q]; fa[q]=fa[np]=nq; 37 while (son[p][x]==q) son[p][x]=nq, p=fa[p]; 38 } 39 } 40 } 41 42 void Update(int &now,int l,int r,int x) 43 { 44 if (!now) now=++sgt_num; 45 Segt[now].val++; 46 if (l==r) return; 47 int mid=(l+r)>>1; 48 if (x<=mid) Update(Segt[now].ls,l,mid,x); 49 else Update(Segt[now].rs,mid+1,r,x); 50 } 51 52 int Merge(int x,int y) 53 { 54 if (!x || !y) return x|y; 55 int now=++sgt_num; 56 Segt[now].ls=Merge(Segt[x].ls,Segt[y].ls); 57 Segt[now].rs=Merge(Segt[x].rs,Segt[y].rs); 58 Segt[now].val=Segt[x].val+Segt[y].val; 59 return now; 60 } 61 62 int Query(int now,int l,int r,int l1,int r1) 63 { 64 if (!now) return 0; 65 if (l>r1 || r<l1) return 0; 66 if (l1<=l && r<=r1) return Segt[now].val; 67 int mid=(l+r)>>1; 68 return Query(Segt[now].ls,l,mid,l1,r1)+Query(Segt[now].rs,mid+1,r,l1,r1); 69 } 70 71 void DFS(int x) 72 { 73 if (pos[x]) Update(Root[x],1,n,pos[x]); 74 for (int i=head[x]; i; i=edge[i].next) 75 { 76 DFS(edge[i].to); 77 Root[x]=Merge(Root[x],Root[edge[i].to]); 78 } 79 } 80 81 void DP(int x) 82 { 83 for (int i=head[x]; i; i=edge[i].next) 84 { 85 int y=edge[i].to; 86 if (x==1) f[y]=1, top[y]=y; 87 else if (Query(Root[top[x]],1,n,pos[y]-step[y]+step[top[x]],pos[y]-1)) 88 f[y]=f[x]+1, top[y]=y; 89 else f[y]=f[x], top[y]=top[x]; 90 DP(edge[i].to); 91 ans=max(ans,f[edge[i].to]); 92 } 93 } 94 95 int main() 96 { 97 scanf("%d%s",&n,s); 98 for (int i=0; i<n; ++i) Insert(s[i]-a,i+1); 99 for (int i=2; i<=cnt; ++i) add(fa[i],i); 100 DFS(1); DP(1); 101 printf("%d\n",ans); 102 }

CF700E:Cool Slogans(後綴自動機,線段樹合並)