[cf700E]Cool Slogans

建立SAM,假设$s_{i}$在SAM中的位置为$a_{i}$以及$l_{i}=|s_{i}|$,通过$(a_{i},l_{i})$即可确定$s_{i}$,也即可判定是否合法

更具体的,即要求$forall 2le ile 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} e 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 2le ile k,|[x-l_{i-1}+l_{i},x]cap R_{a_{i}}|ge 2$,根据parent树的性质,有$xin R_{i}$,因此条件又即$[x-l_{i-1}+l_{i},x)cap R_{a_{i}} e empty$

事实上,$forall 2le ile k,[x-len_{a_{i-1}}+len_{a_{i}},x)cap R_{a_{i}} e 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}$时其非空,任取$kin [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$的不等式)且$xin R_{a_{j-1}}$,因此$R'subset R_{a_{j-1}}$

不妨取$yin complement_{R_{a_{j-1}}}R'$,由于$y otin R'$,即有$s[y-(x-k+len_{a_{j}}),y] e s[k-len_{a_{j}},x]$

另一方面,由于$x,yin 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'] e 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$的定义有$kin 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}}] e 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(nlog^{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
原文地址:https://www.cnblogs.com/PYWBKTDA/p/14804732.html