标签:first 开始 rom max 连接 while 字符串 字符 std
【题意】给定只含小写字母的字符串s,定义价值为回文子串的长度*出现次数,求最大价值。n<=3*10^5。
【算法】回文树
【题解】回文树上一个点的被访问次数是其作为最长回文子串的出现次数。
将fail边反向连接建树后,每个点的子树访问次数和就是这个回文子串的出现次数,可以dfs解决。
注意:要从-1点开始dfs才能保证到达所有点。记得开long long。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int maxn=600010; int n,tot,len[maxn],fail[maxn],length,sz,nownode,ch[maxn][30],first[maxn],size[maxn]; ll ans=0; struct edge{int v,from;}e[maxn]; char s[maxn]; int getfail(int x){while(s[length-len[x]-1]!=s[length])x=fail[x];return x;} void build(){ int y=s[++length]-‘a‘; int x=getfail(nownode); if(!ch[x][y]){ len[++sz]=len[x]+2; fail[sz]=ch[getfail(fail[x])][y]; ch[x][y]=sz; } size[ch[x][y]]++; nownode=ch[x][y]; } void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;} int dfs(int x){ ll sum=size[x]; for(int i=first[x];i;i=e[i].from){ sum+=dfs(e[i].v); } ans=max(ans,sum*len[x]); return sum; } int main(){ scanf("%s",s+1); n=strlen(s+1); len[0]=0;fail[0]=1; len[1]=-1;fail[1]=1; length=0; sz=1;//!!! for(int i=1;i<=n;i++)build(); insert(1,0); for(int i=2;i<=sz;i++){ insert(fail[i],i); } dfs(1);//1 printf("%lld",ans); return 0; }
标签:first 开始 rom max 连接 while 字符串 字符 std
原文地址:http://www.cnblogs.com/onioncyc/p/6950869.html