回文树

回文树:可以用个树表示一个串里面所有为回文串的字符。
贴一个大佬的理解:
https://blog.csdn.net/qq_36551189/article/details/79245675

我感觉跟后缀自动机有点像,分成两个树,长度为奇数的回文,长度为偶数的回文,树上的节点就代表从跟出发,炎沿树上的边走,得到的就是回文的一半。
构造:每个节点有个fail指针,指向当先串的最长后缀为一个回文串,那么可以通过get_fail函数得到cur这个以当前位置为结尾能得到的最长回文,
构造当前节点的fail:对fail[cur]求一个get_fail就可以了,当前长度是len[cur]+2。
这里有个很巧妙的地方,0作为偶数串的根,1作为奇数串的根,len[1] 设为-1。fail[0] = 1,这样如果一个串找不到cur的话,就可以把自己作为一个奇数回文串了。
复杂度的证明:设当前节点的深度为x,那么getfail得到的cur节点的深度一定小于last,x节点深度为cur深度+1,每次操作的时间主要在getfail中,每次getfail最少让节点的深度减1,那么没操作的次数最多为last的深度减去cur的深度再加1,因为x节点再cur下面,又因为last会被赋值为x所在的节点,所以操作次数就是last前后位置深度差+2,所以可以证明操作次数最多为2len(len为字符串长度),如果这里不理解可以去看一下势能摊还分析法。
这事第一个getfailed的复杂度。
第二个getfail的复杂度证明:把getfail想象成一个树,可以吧i,当做getfail(i)的一个儿子,那么跟第一个getfail的情况就一样了,所以可以证明复杂度在2
len之内。
貌似回文树最大时间就在于newnode的初始化了,,,

#include
using namespace std;
const int N = 2e6+100;
const int mod = 1e9+7;
int qp[N];
struct Palindromic_Tree{
    int s[N],nex[N][10],fail[N],last,n,cnt[N],len[N],tp;
    int ret[N];
    int newnode(int x){
        for(int i = 0;i< 10;i ++) nex[tp][i] =0;
        cnt[tp] = 0;
        len[tp] = x;
        return tp++;
    }

    void init(){
        tp = n = last = 0;
        newnode(0);
        newnode(-1);
        fail[0] = 1;
        s[n] = -1;
    }
    int get_fail(int x){
        while(s[n-len[x]-1] != s[n]) x= fail[x];
        return x;
    }
    void add(int x){
        s[++n] = x;
        int cur = get_fail(last);
        if(!nex[cur][x]){
            int np = newnode(len[cur]+2);
            fail[np] = nex[get_fail(fail[cur])][x];
            nex[cur][x] = np;
        }
        last = nex[cur][x];
        cnt[last]++;
    }

    int query(){
        ret[0] =0,ret[1] = 0;
        int ans = 0;
        for(int i = 0;i < tp;i ++){
            //cout << i <<' '<< ret[i] << endl;
            ans += ret[i];
            ans %= mod;
            for(int j = 0;j < 10;j ++){
                if(nex[i][j] == 0) continue;
                //cout <

你可能感兴趣的:(算法理解)