学习链接:点击进入
介绍
回文树通常用来解决一些回文串问题
空间复杂度 O( N * 字符集大小 )
时间复杂度 O( N * log( 字符集大小 ))
功能
-
求串 S 前缀 0 ~ i 内本质不同回文串的个数(两个串长度不同或者长度相同且至少有一个字符不同便是本质不同)
-
求串 S 内每一个本质不同回文串出现的次数
-
求串 S 内回文串的个数
-
求以下标 i 结尾的回文串的个数
存储
-
len [ i ] 表示编号为 i 的节点表示的回文串的长度(一个节点表示一个回文串)
-
next [ i ] [ c ] 表示编号为 i 的节点表示的回文串在两边添加字符 c 以后变成的回文串的编号(和字典树类似)。
-
fail [ i ] 表示节点 i 失配以后跳转不等于自身的节点 i 表示的回文串的最长后缀回文串(和AC自动机类似)。
-
cnt [ i ] 表示节点 i 表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)
-
num [ i ] 表示以节点 i 表示的最长回文串的最右端点为回文串结尾的回文串个数。
-
last 指向新添加一个字母后所形成的最长回文串表示的节点。
-
S [ i ] 表示第 i 次添加的字符(一开始设 S [ 0 ] = -1(可以是任意一个在串 S 中不会出现的字符))。
-
p 表示添加的节点个数。
-
n 表示添加的字符个数。
初始化
回文树初始有两个根节点,0 表示偶数长度串的根,1 表示奇数长度串的根。
len [ 0 ] = 0,len [ 1 ] = -1,last = 0,S [ 0 ] = -1,n = 0,p = 2( 添加了节点 0 、1 )。
代码1( 邻接矩阵版 )
const int maxn=1e5+10;
const int N=26;
/******邻接矩阵******/
struct Palindromic_Tree
{
int next[maxn][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[maxn];//fail指针,失配后跳转到fail指针指向的节点
int cnt[maxn];//表示节点i表示的回文串出现的次数
int num[maxn];//表示以节点i表示的回文串的最右端点为回文串结尾的回文串个数。
int len[maxn];//len[i]表示节点i表示的回文串的长度
int S[maxn];//存放添加的字符
int last;//指向上一个字符所在的节点,方便下一次add
int n;//字符数组指针
int p;//节点指针
int newnode(int l)//新建节点
{
for(int i=0;i<N;++i)
next[p][i]=0;
cnt[p]=0;
num[p]=0;
len[p]=l;
return p++;
}
void init()//初始化
{
p=0;
newnode(0);//0表示偶数长度串的根,len[0]=0
newnode(-1);//1表示奇数长度串的根,len[1]=-1
last=0;
n=0;
S[n]=-1;//开头放一个字符集中没有的字符,减少特判
fail[0]=1;
}
int get_fail(int x)//和KMP一样,失配后找一个尽量最长的
{
while(S[n-len[x]-1]!=S[n])
x=fail[x];
return x;
}
void add(int c)
{
c-='a';
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];//和AC自动机一样建立fail指针,以便失配后跳转
next[cur][c]=now;
num[now]=num[fail[now]]+1;
}
last=next[cur][c];
cnt[last]++;
}
void count()
{
for(int i=p-1;i>=0;--i)
cnt[fail[i]]+=cnt[i] ;
//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
}
}patree;
代码2( 邻接表版 )
const int maxn=1e5+10;
/*邻接表*/
struct Palindromic_Tree
{
vector<pair<int,int>>next[maxn];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[maxn];//fail指针,失配后跳转到fail指针指向的节点
int cnt[maxn];//表示节点i表示的回文串出现的次数
int num[maxn];//表示以节点i表示的回文串的最右端点为回文串结尾的回文串个数。
int len[maxn];//len[i]表示节点i表示的回文串的长度
int S[maxn];//存放添加的字符
int last;//指向上一个字符所在的节点,方便下一次add
int n;//字符数组指针
int p;//节点指针
int newnode(int w)
{
next[p].clear();
cnt[p]=num[p]=0;
len[p]=w;
return p++;
}
void init()
{
p=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 add(int c)
{
c-='a';
S[++n]=c;
int cur=get_fail(last);
int flag=0;
for(int i=0;i<next[cur].size();++i)
{
if(next[cur][i].first==c)
{
last=next[cur][i].second;
cnt[last]++;
return ;
}
}
int now=newnode(len[cur]+2);
int fi=get_fail(fail[cur]);
flag=0;
for(int i=0;i<next[fi].size();i++)
{
if(next[fi][i].first==c)
{
flag=next[fi][i].second;
break;
}
}
fail[now]=flag;
next[cur].push_back(make_pair(c,now));
num[now]=num[flag]+1;
last=now;
cnt[now]++;
}
void count()
{
for(int i=p-1;i>1;--i)
cnt[fail[i]] += cnt[i];
}
}patree;