建立SAM,假设$s_{i}$在SAM中的位置为$a_{i}$以及$l_{i}=|s_{i}|$,通过$(a_{i},l_{i})$即可确定$s_{i}$,也即可判定是否合法
更具体的,即要求$\forall 2\le i\le k,|[x-l_{i-1}+l_{i},x]\cap R_{a_{i}}|\ge 2$(其中$x$为$R_{a_{i-1}}$中某个元素,以下默认)
(实际上对于$R_{a_{i-1}}$中每一个元素,上述判定结果都是相同的,具体证明略,因此$x$任取某一个即可)
显然$a_{i}\ne a_{i-1}$,否则取$x$为$R_{a_{i-1}}$(也即$R_{a_{i}}$)中最小值显然不满足
再不妨强制$s_{i}$是$s_{i-1}$的后缀,显然可以通过缩短$s_{i-1}$来满足
由此,即得到了$a_{i}$必然是$a_{i-1}$在parent树上的祖先(严格),$a_{1},a_{2},..,a_{k}$是parent树上从底向上(不包含根)的链上若干个节点(不要求连续),那么不妨确定这些节点,并判断是否合法
也即判定是否存在$l_{i}$满足$l_{i}\in (len_{fa_{a_{i}}},len_{a_{i}}]$且$\forall 2\le i\le k,|[x-l_{i-1}+l_{i},x]\cap R_{a_{i}}|\ge 2$,根据parent树的性质,有$x\in R_{i}$,因此条件又即$[x-l_{i-1}+l_{i},x)\cap R_{a_{i}}\ne \empty$
事实上,$\forall 2\le i\le k,[x-len_{a_{i-1}}+len_{a_{i}},x)\cap R_{a_{i}}\ne \empty$是存在$l_{i}$的充要条件
(下面的证明可能不太容易理解,可以直接跳过看最后的解释)
首先,充分性显然,其即$l_{i}=len_{a_{i}}$时的条件,若其满足取$l_{i}=len_{a_{i}}$即存在
其次,当其在$i=j$时不满足,来证明不论$l_{j-1}$和$l_{j}$取什么,都有$[x-l_{j-1}+l_{j},x)\cap R_{a_{j}}=\empty$
反证法,假设某组$l_{j-1}$和$l_{j}$时其非空,任取$k\in [x-l_{j-1}+l_{j},x)\cap R_{a_{j}}$,根据前面的条件,可以得到$k$的一个范围:$x-len_{a_{j-1}}+l_{j}\le k<x-len_{a_{j-1}}+len_{a_{j}}$
由此,考虑$s[k-len_{a_{j}},x]$,令其right集合为$R'$,由于其长度大于$len_{a_{j-1}}$(根据$k$的不等式)且$x\in R_{a_{j-1}}$,因此$R'\subset R_{a_{j-1}}$
不妨取$y\in \complement_{R_{a_{j-1}}}R'$,由于$y\notin R'$,即有$s[y-(x-k+len_{a_{j}}),y]\ne s[k-len_{a_{j}},x]$
另一方面,由于$x,y\in R_{a_{j-1}}$,因此$s(y-len_{a_{j-1}},y]=s(x-len_{a_{j-1}},x]$
显然可以在第一个不等式中,两边同时去掉一个长度不超过$len_{a_{j-1}}$的后缀,由于这个后缀是相同的,因此前半部分有不同,即仍然不等
去掉的后缀长度为$x-k$,令$y'=y-(x-k)$,即有$s[y'-len_{a_{j}},y']\ne s[k-len_{a_{j}},k]$
类似地,也在第二个等式中去掉这个后缀,即$s(y-len_{a_{j-1}},y']=s(x-len_{a_{j-1}},k]$
进一步的,有$x-len_{a_{j-1}}\le k-l_{j}$,即$s(y'-l_{j},y']=s(k-l_{j},k]$(同时去掉了一个前缀)
根据$k$的定义有$k\in R_{a_{j}}$,又因为$l_{j}\in (len_{fa_{a_{j}}},len_{a_{j}}]$,即$s(k-l_{j},k]$的right集合为$R_{a_{j}}$,因此$k,y'\in R_{a_{j}}$,即与$s[y'-len_{a_{j}}]\ne s[k-len_{a_{j}},k]$矛盾
综上,即得证
通俗的来说,一定是形如ab和bab,原串为babbab,在abbab中前者可以接而后者不能接,那么babbab和abbab的right集合必然相同,与abbab是其right集合($R_{a_{j-1}}$)中最长串矛盾
上面证明的思路也即是类似,根据abbab最长而abbab和babbab的right集合不同(且前者严格包含后者,即前面$R'\subset R_{a_{j-1}}$),原串应该是babbabcabbab,取abbab出现且babbab没出现(即最后一个位置),根据abbab出现可以推出在开头的位置出现,而babbab没出现可以推出bab在同样的位置没出现,即与ab和bab的right集合相同矛盾
根据这个结论,即可直接根据$a_{i}$来简单的判定,那么在parent树上dp,令$f_{i}$表示以$i$为根的子树中,强制其选择$i$的最多能选的节点数
由于parent树的根节点不能选,答案是$\max_{x为parent树根节点的儿子}f_{x}$
考虑转移,根据前面的判定方式,显然有$f_{i}=\max_{j在i子树中,[x-len_{j}+len_{i},x)\in R_{i}}f_{j}+1$
显然上述条件在$i$变为$i$的父亲一定仍然满足,因此有$f_{i}\ge \max_{x为i的儿子}f_{x}$,不妨先将$f_{i}$赋值为后者,那么所有能转移到$i$某个儿子的$j$就不需要考虑了,仅需要考虑恰能转移到$i$的位置$j$
先用线段树合并预处理出每一个位置上的right集合(在合并时新建节点即可),再倍增对每个$j$查找$i$即可,总复杂度为$o(n\log^{2}n)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 400005 4 #define mid (l+r>>1) 5 struct Edge{ 6 int nex,to; 7 }edge[N]; 8 int V,VV,E,n,lst,nex[N],len[N],R[N],ch[N][26],head[N],fa[N][21],rt[N],f[N*50],ls[N*50],rs[N*50],dp[N]; 9 char s[N]; 10 void add(int c){ 11 int p=lst,np=lst=++V; 12 len[np]=len[p]+1; 13 while ((p)&&(!ch[p][c])){ 14 ch[p][c]=np; 15 p=nex[p]; 16 } 17 if (!p)nex[np]=1; 18 else{ 19 int q=ch[p][c]; 20 if (len[q]==len[p]+1)nex[np]=q; 21 else{ 22 int nq=++V; 23 nex[nq]=nex[q]; 24 nex[q]=nex[np]=nq; 25 len[nq]=len[p]+1; 26 memcpy(ch[nq],ch[q],sizeof(ch[q])); 27 while ((p)&&(ch[p][c]==q)){ 28 ch[p][c]=nq; 29 p=nex[p]; 30 } 31 } 32 } 33 } 34 void update(int &k,int l,int r,int x){ 35 if (!k)k=++VV; 36 f[k]++; 37 if (l==r)return; 38 if (x<=mid)update(ls[k],l,mid,x); 39 else update(rs[k],mid+1,r,x); 40 } 41 int query(int k,int l,int r,int x,int y){ 42 if ((!k)||(l>y)||(x>r))return 0; 43 if ((x<=l)&&(r<=y))return f[k]; 44 return query(ls[k],l,mid,x,y)+query(rs[k],mid+1,r,x,y); 45 } 46 int find(int k,int l,int r){ 47 if (l==r)return l; 48 if (f[ls[k]])return find(ls[k],l,mid); 49 return find(rs[k],mid+1,r); 50 } 51 int merge(int x,int y){ 52 if ((!x)||(!y))return x+y; 53 int k=++VV; 54 ls[k]=merge(ls[x],ls[y]); 55 rs[k]=merge(rs[x],rs[y]); 56 f[k]=f[ls[k]]+f[rs[k]]; 57 return k; 58 } 59 void add(int x,int y){ 60 edge[E].nex=head[x]; 61 edge[E].to=y; 62 head[x]=E++; 63 } 64 void dfs(int k){ 65 fa[k][0]=nex[k]; 66 if (k==1)fa[k][0]=1; 67 for(int i=1;i<=20;i++)fa[k][i]=fa[fa[k][i-1]][i-1]; 68 for(int i=head[k];i!=-1;i=edge[i].nex){ 69 dfs(edge[i].to); 70 rt[k]=merge(rt[k],rt[edge[i].to]); 71 } 72 R[k]=find(rt[k],1,n); 73 } 74 void calc(int k){ 75 int s=1; 76 for(int i=head[k];i!=-1;i=edge[i].nex){ 77 calc(edge[i].to); 78 s=max(s,dp[edge[i].to]); 79 } 80 if (k==1)dp[k]=s; 81 else dp[k]=max(dp[k],s); 82 int kk=k; 83 for(int i=20;i>=0;i--){ 84 int x=fa[kk][i]; 85 if (!query(rt[x],1,n,R[k]-len[k]+len[x],R[k]-1))kk=x; 86 } 87 kk=nex[kk]; 88 if (kk)dp[kk]=max(dp[kk],dp[k]+1); 89 } 90 int main(){ 91 scanf("%d%s",&n,s+1); 92 V=lst=1; 93 for(int i=1;i<=n;i++){ 94 add(s[i]-'a'); 95 update(rt[lst],1,n,i); 96 } 97 memset(head,-1,sizeof(head)); 98 for(int i=2;i<=V;i++)add(nex[i],i); 99 dfs(1); 100 calc(1); 101 printf("%d",dp[1]); 102 }View Code