回文树学习笔记(模板)

好好理解了回文树。

理解后的感觉:为啥子之前会觉得很复杂?Orz

根据自己理解改了个自己能看懂的模板

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+50;

int last,tot,n;
int next[maxn][27],len[maxn],fail[maxn],s[maxn],cnt[maxn]; 
/*
n 是字符串的长度 
last 是当前节点的上一节点 记录信息的节点编号从1开始
next[i][j] 是 i节点左右添加字符x后所形成的新的字符串的 节点标号
len[i] 是i节点所代表的字符串的长度
fail[i] 是节点i的最长回文后缀的 节点标号
s[i] 是字符串的第i个字符 
cnt[i] 是i节点所代表的的回文串在整个字符串里出现的次数 
*/
inline void init()
{
    s[0]=-1;fail[0]=1;last=0;
    len[0]=0;len[1]=-1;tot=1;
}
/*
零号节点的最长回文后缀是指向1号节点 而1号节点的长度是 len[1]=1;
这个操作有利于:
当添加第一个字符时(节点2) 新的回文串长度 恰好等于:-1+2=1
*/
inline int getfail(int x,int id)
{
    while(s[id-len[x]-1]!=s[id])x=fail[x];
    return x;
}
/*
从x节点往下找 找回文后缀恰好回文后缀的前一个字符等于要添加的字符
使得能构成一个新的回文后缀
若找不到,则结果是回到1号节点 然后再添加新字符 则形成了一个长度为1的回文串
len[1]=-1有利于
在找不到的情况下 最后一定能找到1号节点满足:
(s[id-len[1]-1]=s[id-(-1)-1])==s[id] 
*/ 
inline int newnode(int x)
{
    len[++tot]=x;
    return tot;
}
inline int solve()
{
    int i,p,q;
    init();
    for(i=1;i<=n;i++)
    {
        p=getfail(last,i);
        //找到上一节点可以添加s[i]字符的回文后缀所在的节点标号 p 
        if(next[p][s[i]]==0)//如果 p节点没有过两边添加s[i]的记录 
        {
            printf("%d %d ",i,q);
            q=newnode(len[p]+2);//新字符串为p串两边添加s[i]
            fail[q]=next[getfail(fail[p],i)][s[i]];
            //q的最长回文后缀是 p的满足前一个字符是s[i]的回文后缀再加字符s[i]的回文串所在节点 没有则为节点0
            next[p][s[i]]=q;
            
            for(int j=i-len[q]+1;j<=i;j++)printf("%c",s[j]+'a');
            putchar(10);
        }
        last=next[p][s[i]];//将当前节点更新到上一节点
        cnt[next[p][s[i]]]++;//这个回文串出现的记录+1 
    }
    for(i=tot;i>1;i--)
    {
        cnt[fail[i]]+=cnt[i];
        /*
        i节点出现必是包含其回文后缀的出现
        所以最后需要更新回文后缀出现的次数 
        因为之前更新的节点所代表的串 都不会是别的串的最长回文后缀 
        */ 
    }
}
int main()
{
    char s1[maxn];
    scanf("%s",s1);
    n=strlen(s1);
    for(int i=1;i<=n;i++)s[i]=s1[i-1]-'a';
    solve();
}

2019-09-10 22:39:54

猜你喜欢

转载自www.cnblogs.com/kkkek/p/11503579.html