回文树

回文树:可以用个树表示一个串里面所有为回文串的字符。
贴一个大佬的理解:
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<bits/stdc++.h>
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 <<i << ' ' <<j << ' '<<nex[i][j] << endl;
                if(i == 1){
                    ret[nex[i][j]] = j;
                }
                else{
                    ret[nex[i][j]] = (1LL*10*ret[i]+1ll*(qp[len[nex[i][j]]-1]+1)*j)%mod;
                }
            }
        }
        return ans;
    }

}pat;

char st[N];
int main(){
    qp[0] = 1;
    for(int i = 1;i < N;i ++) qp[i] = 1LL*qp[i-1]*10%mod;
    scanf("%s",st);
    pat.init();
    int len = strlen(st);
    for(int i = 0;i < len;i ++){
        pat.add(st[i]-'0');
    }
    printf("%d\n",pat.query());
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值