zoukankan      html  css  js  c++  java
  • [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
  • 相关阅读:
    POJ 1915 简单 广搜题
    poj 2479 最大子数段
    poj 1321 深搜题
    hdu 1024 最大子段和
    .net 学习总结
    [转]SharpDevelop源码分析 (二、主程序+隐藏的初始化)
    工作六年经验分享:软件工程师如何修炼(转)
    Ajax命名空间一个获取指定的页面元素的快捷方式——$get()
    [转]SharpDevelop代码分析 (一、序+基本概念)
    [转]SharpDevelop源码分析 (三、插件系统)
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14804732.html
Copyright © 2011-2022 走看看