Codechef PLANT Team Trees

Link
首先我们可以发现答案的两个串\(t_1,t_2\)一定是这样的形式:\(t_1\)是一个\(pre/suf\)\(t_2\)紧挨着\(t_1\)
不妨认为\(t_1\)\(pre\),对于\(suf\)只需要把原串翻转之后再做一遍即可。
那么我们可以对于所有的\(s_{1,i}\),计算出最长的满足\(s_{i+1,i+l}\subseteq s_{1,i}\)\(l_i\),答案就是\(\max\limits_{i=1}^n(il_i)\)
考虑在在线构造SAM的途中计算\(l_i\),同时维护\(x_i\)表示\(s_{i+1,i+l}\)\(s_{1,i}\)的SAM上对应的位置。
注意到\(s_{i,i+l-1}\subseteq s_{1,i-1}\),因此\(s_{i+1,i+l-1}\subseteq s_{1,i}\),也就是说\(l_i\ge l_{i-1}-1\)
因此先令\(l_i=l_{i-1}-1,x_i=x_{i-1}\)
注意到\(s_{i,i+l-1}\)可能会被划分到\(link_{x_i}\)\(endpos\)去,因此如果\(l_{i-1}\le len_{link_{x_i}}\),那么\(x_i=link_{x_i}\)
然后\(s_{i+1,i+l-1}\)相比\(s_{i,i+l-1}\)可能又会被划分到\(link_{x_i}\)\(endpos\)去,因此如果\(l_i\le len_{link_{x_i}}\),那么\(x_i=link_{x_i}\)
然后再暴力扩展\(l_i\)并移动\(x_i\)
因为\(l\)指针的总移动量是\(O(n)\)的,所以这样做的时间复杂度是均摊\(O(n)\)的。

#include<cstdio>
#include<cstring>
#include<algorithm>
using i64=long long;
const int N=4000007;
i64 max(i64 a,i64 b){return a>b? a:b;}
char str[N];int n,cnt,now,ch[N][26],link[N],len[N];i64 ans;
void extend(int c)
{
    int p=now,q,copy;len[now=++cnt]=len[p]+1;
    for(;~p&&!ch[p][c];p=link[p]) ch[p][c]=now;
    if(!~p) return void();
    if(len[q=ch[p][c]]==len[p]+1) return link[now]=q,void();
    link[copy=++cnt]=link[q],memcpy(ch[copy],ch[q],sizeof ch[q]),len[copy]=len[p]+1,link[now]=link[q]=copy;
    for(;~p&&ch[p][c]==q;p=link[p]) ch[p][c]=copy;
}
void solve()
{
    link[0]=-1;
    for(int i=1,x=0,l=0;i<=n;++i)
    {
	extend(str[i]-'a');
	if(l&&l<=len[link[x]]) x=link[x];
	if(l&&--l<=len[link[x]]) x=link[x];
	while(i+l<n&&ch[x][str[i+l+1]-'a']) x=ch[x][str[i+(++l)]-'a'];
	ans=max(ans,1ll*i*l);
    }
}
void clear(){memset(ch,0,sizeof ch),memset(link,0,sizeof link),memset(len,0,sizeof len),now=cnt=0;}
int main(){scanf("%d%s",&n,str+1),solve(),std::reverse(str+1,str+n+1),clear(),solve(),printf("%lld",ans);}
posted @ 2020-02-15 20:22  Shiina_Mashiro  阅读(163)  评论(0编辑  收藏  举报