学习博客:https://www.cnblogs.com/nbwzyzngyl/p/8260921.html
博客中讲的很清楚了,在此我就不过多赘述了
len[i], 以i结尾的最长回文子串的长度
cnt[i]:以i结尾的最长回文子串相同的子串的个数 在count后得到全部
num[i] 表示以i结尾的回文串的种类数
str[] 存放添加的字符
fail[] 失配后跳转到的不等于自身的最长后缀回文子串。
pos[] 表示当前最长回文子串的结束位置
ch[][] 类似于字典树,指向当前字符串在两端同时加上一个字符
last 最新添加的回文节点
tot 总的节点个数
n表示添加的字符个数
char str[maxn];
ll fail[maxn],len[maxn],last,ch[maxn][26],n,num[maxn],cnt[maxn],tot,pos[maxn];
//len[i], 以i结尾的最长回文子串的长度
//cnt[i]:以i结尾的最长回文子串相同的子串的个数 在count后得到全部
//num[i] 表示以i结尾的回文串的种类数
//str[] 存放添加的字符
//fail[] 失配后跳转到的不等于自身的最长后缀回文子串。
//pos[] 表示当前最长回文子串的结束位置
//ch[][] 类似于字典树,指向当前字符串在两端同时加上一个字符
//last 最新添加的回文节点
// tot 总的节点个数
// n表示添加的字符个数
ll newnode(ll x)
{
for(ll i = 0; i < 26; i++) //for 非多次调用回文树可省略
ch[tot][i] = 0;
cnt[tot] = 0;
num[tot] = 0;
len[tot] = x;
return tot++;
}
void init()
{
tot = 0;
last = 0;
newnode(0); //偶串根节点
newnode(-1); //奇串根节点
str[0] = '#'; //0号位置设置成一个不会出现的字符
fail[0] = 1; //两个根节点互指
fail[1] = 0;
return ;
}
ll get_fail(ll p,ll x)
{
while(str[ x-len[p]-1 ] != str[x] )
p = fail[p];
return p;
}
void count()
{
for(ll i = tot-1; i >= 0; i--)
cnt[ fail[i] ] += cnt[i];
//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
return ;
}
void build() //构建回文树
{
for(ll i = 1; i <= n; i++)
{
ll tmp = get_fail(last,i);
ll s = str[i]-'a';
if(!ch[tmp][s])
{
ll now = newnode(len[tmp]+2);
fail[now] = ch[ get_fail( fail[tmp],i ) ][ s ];
ch[tmp][s] = now;
num[now] = num[fail[now]]+1;
}
last = ch[tmp][s];
pos[last] = i;
cnt[last]++;
}
return ;
}