bzoj 3676: [Apio2014]回文串

Description

考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出
现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最
大出现值。

Input

输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。

Output


输出一个整数,为逝查回文子串的最大出现值。

Sample Input

【样例输入l】
abacaba

【样例输入2]
www

Sample Output

【样例输出l】
7

【样例输出2]
4

HINT

一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。

在第一个样例中,回文子串有7个:a,b,c,aba,aca,bacab,abacaba,其中:

● a出现4次,其出现值为4:1:1=4

● b出现2次,其出现值为2:1:1=2

● c出现1次,其出现值为l:1:l=l

● aba出现2次,其出现值为2:1:3=6

● aca出现1次,其出现值为1=1:3=3

●bacab出现1次,其出现值为1:1:5=5

● abacaba出现1次,其出现值为1:1:7=7

故最大回文子串出现值为7。

【数据规模与评分】

数据满足1≤字符串长度≤300000。

Source

颓了好久了,终于想着来学一点新东西了;

其实回文自动机(or回文树)还是算一种比较simple的数据结构,其中每一个点代表的是一个回文串;

具体是用来统计:本质不同的回文串个数,以及,每个回文串的出现次数;

主要是要弄清几个数组的含义:

1.len[i]表示编号为i的节点表示的回文串的长度(一个节点表示一个回文串)

2.next[i][c]表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号

3.fail[i]表示节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串(和AC自动机类似)。

4.cnt[i]表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)

5.num[i]表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数。

6.last指向新添加一个字母后所形成的最长回文串表示的节点。

(蒯的.....)

具体的话可以参见lcf2000的博客...

一开始有两个点,0表示偶数长度,1表示奇数长度的;

主要就是不断跳fail指针,直到S[n - len[last] - 1]==S[n](即上一个串-1的位置和新添加的位置是否相同,相同则说明构成回文)

最后找到了一个cur点,nxt[cur][c]有值说明以前已经有过这个回文串了,只需把次数加1;

如果不存在,则说明要新建一个节点表示新的回文串,且这个回文串的len显然等于len[cur]+2(len[1]=-1可以省一个特判),然后更新一下fail;

更新fail就是找到第一个使得S[n - len[last] - 1] == S[n]的last。

最后统计答案的时候从叶子节点开始加即可

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=500050;
int gi()
{
  int x=0,flag=1;
  char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();}
  while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x*flag;
}
int len[N],nxt[N][30],fail[N],tt,cnt[N],last;
char s[N];
void insert(int c,int n){
    int cur=last;
    while(s[n-len[cur]-1]!=s[n]) cur=fail[cur];
    if(!nxt[cur][c]){
	int now=++tt,la=fail[cur];len[now]=len[cur]+2;
	while(s[n-len[la]-1]!=s[n]) la=fail[la];
	fail[now]=nxt[la][c],nxt[cur][c]=now;
    }
    last=nxt[cur][c];cnt[last]++;
}
int main(){
    len[++tt]=-1;fail[0]=1;
    scanf("%s",s+1);int l=strlen(s+1);
    for(int i=1;i<=l;i++) insert(s[i]-'a',i);
    ll ans=0;for(int i=tt;i>1;i--) cnt[fail[i]]+=cnt[i],ans=max(ans,1ll*len[i]*cnt[i]);
    printf("%lld\n",ans);
}

 

posted @ 2017-05-28 20:31  qt666  阅读(107)  评论(0编辑  收藏  举报