首先,迴文樹有何功能?
假設我們有一個串S,S下標從0開始,則迴文樹能做到如下幾點:
1.求串S前綴0~i內本質不同迴文串的個數(兩個串長度不同或者長度相同且至少有一個字符不同便是本質不同)
2.求串S內每一個本質不同迴文串出現的次數
3.求串S內迴文串的個數(其實就是1和2結合起來)
4.求以下標i結尾的迴文串的個數
建立的迴文樹代表的都是以s[i]結尾的最長迴文串。
(因此cnt數組原本表示的是以某以字符串結尾的本質不同(迴文串相同,位置不同 即同種迴文串)串的個數(建樹時求出的不是完全的,最後count()函數跑一遍以後纔是正確的)
迴文樹每一個節點都代表一種字符串
1.len[i]表示編號爲i的節點表示的迴文串的長度(一個節點表示一個迴文串)
2.tree[i][c]表示編號爲i的節點表示的迴文串在兩邊添加字符c以後變成的迴文串的編號(和字典樹類似)。
3.fail指針指向以該字符結尾的次長會文串(和AC自動機類似)。
4.cnt[i]表示節點i表示的本質不同(同種)的迴文串的個數(建樹時求出的不是完全的,最後count()函數跑一遍以後纔是正確的)
5.num[i]表示以節點i表示的最長迴文串的最右端點爲迴文串結尾的迴文串種類數(也就是fail指針路徑的深度)。(我感覺是不同種類的字符串的個數,因爲num[now] = num[fail[now]] + 1)(以這個串結尾的迴文串的種類)
6.last:上一次添加字符時形成迴文串在迴文樹中的節點座標。
7.s[i]表示第i次添加的字符(一開始設s[0] = -1(可以是任意一個在串S中不會出現的字符))。
8.tot表示添加的節點個數。
9.n表示添加的字符個數。
一開始迴文樹有兩個節點,0表示偶數長度串的根和1表示奇數長度串的根,且len[0] = 0,len[1] = -1,last = 0,S[0] = -1,n = 0,p = 2(添加了節點0、1)
具體思路:
Palindromic Tree——迴文樹
模板
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5;
//fail指針指向以該字符結尾的次長會文串
//next數組用來存放回文樹
//len數組用來存儲樹中每一個節點回文串的長度
//S數組用來記錄添加過的字符,方便以後通過len數組判斷是否是迴文串。
/*cnt[i]:
節點i表示的本質不同的串的個數(建樹時求出的不是完全的,count()加上子節點以後纔是正確的) */
int N;
string S;
struct node{
int len[maxn],tree[maxn[26],fail[maxn],s[maxn],cnt[maxn],num[maxn];
int tot,n,last;
int newnode(int l)
{
len[tot]=l;
return tot++;return tot++;//勿打成++p,因爲此節點爲p,我們應返回p
}
void init()
{
tot=n=last=0;
fill(num,num+maxn,0);
fill(tree[0],tree[0]+maxn*26,0);
fill(cnt,cnt+maxn,0);
newnode(0);
newnode(-1);
s[0]=-1;//S數組用來保存添加過的字符,這個保存時一定要從1開始,不然會出現問題。
fail[0]=1;
fail[1]=1;//這個寫不寫都無所謂,因爲len[1]=-1,這個一定成立,不會再向下找fail了。
}
//這個函數使用來找最長迴文後綴的函數。
int get_fail(int x)
{
while(s[n-len[x]-1]!=s[n]) x=fail[x];
return x;
}
void insert(int c)
{
c-='a';
s[++n]=c;
int cur=get_fail(last);//能與該插入字符構成迴文串的節點。
if(!tree[cur][c])
{
int now=newnode(len[cur]+2);
fail[now]=tree[get_fail(fail[cur])][c];
tree[cur][c]=now;
num[now] = num[fail[now]] + 1 ;
}
last=tree[cur][c];
cnt[last]++;
}
//統計本質相同的迴文串的出現次數
//逆序累加,保證每個點都會比它的父親節點先算完,於是父親節點能加到所有子孫
void count()
{
//原本求得是以節點i結尾的最長的迴文串出現的次數,所以這個
//最長串中一定包含有短的迴文串。
//兒子累加父親的cnt,因爲如果fail[v]=u,則u一定是v的子迴文串!
for(int i=tot-1;i>=0;i++)
{
cnt[fail[i]]+=cnt[i];
}
}
}run;
int main()
{
string S;
cin>>S;
run.init();
N=S.size();
for(int i=0;i<N;i++)
{
run.insert(S[i]);
}
run.count();
}