题意翻译
给出一个长度为$n$的字符串$s[1]$,由小写字母组成。定义一个字符串序列$s[1....k]$,满足性质:$s[i]$在$s[i-1]$ $(i>=2)$中出现至少两次(位置可重叠),问最大的$k$是多少,使得从$s[1]$开始到$s[k]$都满足这样一个性质。
题解
看了一个中午的代码终于弄懂了……$yyb$大佬强无敌……
一开始以为是SAM建好之后直接上$dp$,直接用$parent$树上的儿子节点更新父亲,因为parent树上节点不同说明出现次数必定不同。但交上去一发WA了。才发现自己这种想法不能保证$parent$树上的父亲必定在儿子中出现过超过两次……
还是来说说正解吧。我们先对原串建好SAM,并记录下每一个节点的任意一个$endpos$。考虑一下如何判断一个串是否在另一个串中出现,如果一个串(设串$A$)在另一个串(设串$B$)中出现了大于等于两次,那么在原串的任意位置的串$B$中都出现了两次。
于是考虑一下维护每一个点的$endpos$集合,这个只要用线段树就行了。如果$A$在$B$中出现了两次,那么$A$的$endpos$集合在$[pos[B]-len[B]+len[A],pos[B]]$中出现了至少两次(其中$pos[B]$表示$B$的任意一个$endpos$)。
不难发现有一个$dp$,每一个$parent$树上的父亲节点都可能转移到儿子节点,于是从上到下$dp$。又因为$parent$树上父亲是儿子的严格后缀,所以必然在儿子里出现了一次,那么只要考虑$endpos[A]$中是否有元素在$[pos[B]-len[B]+len[A],pos[B]-1]$中就行了
1 //minamoto 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;} 6 const int N=444444; 7 int fa[N],ch[N][26],l[N],f[N],a[N],c[N],pos[N],top[N];char s[N]; 8 int last=1,cnt=1,n,ans=1; 9 void ins(int c,int k){ 10 int p=last,np=++cnt;last=np,l[np]=l[p]+1,pos[np]=k; 11 for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np; 12 if(!p) fa[np]=1; 13 else{ 14 int q=ch[p][c]; 15 if(l[q]==l[p]+1) fa[np]=q; 16 else{ 17 int nq=++cnt;l[nq]=l[p]+1,pos[nq]=pos[q]; 18 memcpy(ch[nq],ch[q],sizeof(ch[q])); 19 fa[nq]=fa[q],fa[np]=fa[q]=nq; 20 for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 21 } 22 } 23 } 24 void calc(){ 25 for(int i=1;i<=cnt;++i) ++c[l[i]]; 26 for(int i=1;i<=cnt;++i) c[i]+=c[i-1]; 27 for(int i=1;i<=cnt;++i) a[c[l[i]]--]=i; 28 } 29 int L[N*25],R[N*25],rt[N],tot; 30 void modify(int &now,int l,int r,int x){ 31 now=++tot;if(l==r) return; 32 int mid=l+r>>1; 33 if(x<=mid) modify(L[now],l,mid,x); 34 else modify(R[now],mid+1,r,x); 35 } 36 int merge(int x,int y){ 37 if(!x||!y) return x|y; 38 int z=++tot; 39 L[z]=merge(L[x],L[y]); 40 R[z]=merge(R[x],R[y]); 41 return z; 42 } 43 int query(int x,int l,int r,int ql,int qr){ 44 if(!x) return 0;if(ql<=l&&qr>=r) return 1; 45 int mid=l+r>>1; 46 if(ql<=mid&&query(L[x],l,mid,ql,qr)) return 1; 47 if(qr>mid&&query(R[x],mid+1,r,ql,qr)) return 1; 48 return 0; 49 } 50 int main(){ 51 scanf("%d",&n); 52 scanf("%s",s+1); 53 for(int i=1;i<=n;++i) ins(s[i]-'a',i),modify(rt[last],1,n,i); 54 calc(); 55 for(int i=cnt;i>1;--i) rt[fa[a[i]]]=merge(rt[fa[a[i]]],rt[a[i]]); 56 for(int i=2;i<=cnt;++i){ 57 int u=a[i],ff=fa[u]; 58 if(ff==1){f[u]=1,top[u]=u;continue;} 59 int x=query(rt[top[ff]],1,n,pos[u]-l[u]+l[top[ff]],pos[u]-1); 60 if(x) f[u]=f[ff]+1,top[u]=u; 61 else f[u]=f[ff],top[u]=top[ff]; 62 cmax(ans,f[u]); 63 } 64 printf("%d\n",ans); 65 return 0; 66 }