回文树是一个很方便的解决回文串的问题。
他可以求出本质不同的回文串个数。
它的处理方式是这样:
每次一个一个加入,每次加入字符的时候计算个数。
它对每个回文串都存储了一个结点,属性为长度。通过每个回文串,以一条字母边可以指向新的回文串,新的回文串=旧的回文串在两侧加上该字母。
每次找到当前字母结尾的最长的且能够满足新加入的字母和最边缘的字母(该回文串签一个字母)相同的最长回文串。寻找的过程是跳fail指针的过程。
如果该回文串通过该字母产生的新回文串被记录过了,那么不再记录。
如何快速找到上次加入字母过后的最长回文串,显然是上次找到或创建的新回文串。所以我们记录下上次的回文串。找的时候不断跳fail指针即可。
fail指针怎么建立呢?
每次创建新的回文串:新的回文串的fail指针指向自己的最长回文后缀。
但是算法的正确性我无法理解。[所以现在只能记住。想了快十个小时了,还是想不明白。
模板如下:
struct PAM{
int Next[maxn][sigma_size],fail[maxn],len[maxn],S[maxn];
int last,n,p;
ll ans=0;
int Newnode(int x){
for(int i=0;i<sigma_size;i++)Next[p][i]=0;
len[p]=x;
return p++;
}
void init(){
p=0,ans=0;
Newnode(0);Newnode(-1);
last=0,n=0;
S[n]=-1;fail[0]=1;
}
int get_fail(int x){
while(S[n-len[x]-1]!=S[n])x=fail[x];
return x;
}
void insert(int c){
S[++n]=c;
int cur=get_fail(last);
if(!Next[cur][c]){
int now=Newnode(len[cur]+2);
//在这里进行计算:唯一子串只会出现一次
fail[now]=Next[get_fail(fail[cur])][c];
Next[cur][c]=now;
}
last=Next[cur][c];
}
}pm;