正题
题目链接:https://www.luogu.com.cn/problem/CF700E
题目大意
给出一个字符串SSS,求一个最大的kkk使得存在kkk个字符串其中s1s_1s1是SSS的子串,si+1s_{i+1}si+1在sis_isi中出现了至少222次。
解题思路
首先我们需要有两个结论
- si+1s_{i+1}si+1一定是sis_isi的其中一个后缀。因为如果si+1s_{i+1}si+1不是sis_isi的一个后缀,那么sis_isi去掉后面那一部分不会影响匹配数并且更短,也就是更优
- 对于parentsparentsparents树上的一对父子x,yx,yx,y,yyy代表的所以字符串与xxx最长串的匹配数均相等。因为如果有不等的,那么证明yyy中的字符串的出现endposendposendpos集合不同,不符合SAM\text{SAM}SAM的定义,故不成立。
这样我们就可以在SAM\text{SAM}SAM上进行dpdpdp了,因为第一个结论我们可以直接在failfailfail树上dpdpdp,然后第二个结论让我们能够使用每个节点最长的串来进行匹配,因为这不会影响答案。
现在我们需要考虑如何判断一个节点的串是否在另一个它的祖先节点的串中出现了两次,首先作为后缀已经出现了一次,然后只需要判断是否包含一个出现在[posx−lenx+leny,posx−1][pos_x-len_x+len_y,pos_x-1][posx−lenx+leny,posx−1]的串就好了,因为endposendposendpos在这个范围内出现的串一定是与字符串xxx相同的。
用线段树合并维护每个节点包含的串的位置就好了,还有就是要从上往下转移。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=4e5+10,M=N<<6;
int n,cnt,last,ans,c[N],p[N],f[N],top[N];
int rt[N],pos[N],len[N],fa[N],ch[N][26];
char s[N];
struct Seq_Tree{int w[M],ls[M],rs[M],cnt;int Change(int x,int L,int R,int pos){int y=++cnt;w[y]=w[x]+1;if(L==R)return y;int mid=(L+R)>>1;if(pos<=mid)ls[y]=Change(ls[x],L,mid,pos),rs[y]=rs[x];else ls[y]=ls[x],rs[y]=Change(rs[x],mid+1,R,pos);return y;}int Merge(int x,int y,int L,int R){if(!x||!y)return x|y;int p=++cnt;w[p]=w[x]+w[y];if(L==R)return p;int mid=(L+R)>>1;ls[p]=Merge(ls[x],ls[y],L,mid);rs[p]=Merge(rs[x],rs[y],mid+1,R);w[p]=w[ls[p]]+w[rs[p]];return p;}int Ask(int x,int L,int R,int l,int r){if(!x)return 0;if(L==l&&R==r)return w[x];int mid=(L+R)>>1;if(r<=mid)return Ask(ls[x],L,mid,l,r);if(l>mid)return Ask(rs[x],mid+1,R,l,r);return Ask(ls[x],L,mid,l,mid)+Ask(rs[x],mid+1,R,mid+1,r);}
}T;
void Insert(int c){int p=last,np=last=++cnt;len[np]=len[p]+1;for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;if(!p)fa[np]=1;else{int q=ch[p][c];if(len[p]+1==len[q])fa[np]=q;else{int nq=++cnt;len[nq]=len[p]+1;memcpy(ch[nq],ch[q],sizeof(ch[nq]));fa[nq]=fa[q];pos[nq]=pos[q];fa[q]=fa[np]=nq;for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;}}return;
}
int main()
{scanf("%d",&n);cnt=last=1;scanf("%s",s+1);for(int i=1;i<=n;i++)Insert(s[i]-'a'),rt[last]=T.Change(rt[last],1,n,i),pos[last]=i;for(int i=1;i<=cnt;i++)c[len[i]]++;for(int i=1;i<=n;i++)c[i]+=c[i-1];for(int i=1;i<=cnt;i++)p[c[len[i]]--]=i;for(int i=cnt;i>1;i--)rt[fa[p[i]]]=T.Merge(rt[fa[p[i]]],rt[p[i]],1,n);int ans=1;for(int i=2;i<=cnt;i++){int x=p[i],y=fa[x];if(y==1){f[x]=1;top[x]=x;continue;}y=top[y];if(T.Ask(rt[y],1,n,pos[x]-len[x]+len[y],pos[x]-1))f[x]=f[y]+1,top[x]=x;else top[x]=y,f[x]=f[y];ans=max(ans,f[x]);}printf("%d\n",ans);return 0;
}