Colorful String 回文树==回文自动机

题目链接:https://nanti.jisuanke.com/t/41389 

题意: 求字符串s中的所有回文子串的值之和(每个回文串不同字母个数表示它的值);

样例输入复制

abac

样例输出复制

6

解释 abac中有 a, b,  a,  c,  aba 这5个回文子串 其中值得和为 1+1+1+1+2 = 6;

思路: 利用回文树也就是回文自动机 求每个回文字符串的用二进制记录某字符的出现是否存在 最终累加所有回文字符串的出现次数乘它不同字母数相加即可。。。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int mx = 3e5+5;
struct pam {
    int tot, last;
    long long mX, coun[mx];
    char s[mx];
    struct node {
        int len, fail, si, nex[26];
        void newtr(int l, int fl, int sk) {
            memset(nex, 0, sizeof(nex));
            len = l;
            fail = fl;
            si = sk;
        }
    }N[mx];///树里存的是字符串的类型
    void init() {
        N[0].newtr(0, 1, 0);
        N[1].newtr(-1, 1, 0);
        ///两个树根和长度0偶1奇,-1为了表示加2-1为奇数
        memset(coun, 0, sizeof(coun));
        ///记录结尾为某节点的数量
        tot = 1;
    }

    int getfil(int New,int id) {
        while (s[id-N[New].len-1] != s[id]) {
            New = N[New].fail;
            ///找末尾位为s[id-1]的回文后缀从大找到小
        }
        return New;
        ///最终找到的是末尾为s[id]的最大回文字符串
    }

    void insert(int id) {
        last = getfil(last, id);
        int c = s[id]-'a';
        if (!N[last].nex[c]) {
            int k = getfil(N[last].fail, id);
            N[++tot].newtr(N[last].len+2, N[k].nex[c], N[last].si|(1<<c));
            ///长度为+2回文两边都有,找到第二长的以id为结尾的回文串
            N[last].nex[c] = tot;
        }
        last = N[last].nex[c];
        coun[last]++;
    }
    int qry(int x) {
        int sum = 0;
        while (x) {
            if(x%2) sum++;
            x/=2;
        }
        return sum;
    }
    void ans() {
        for (int i = tot; i > 1; --i) {
            coun[N[i].fail] += coun[i];
            ///计算最终的以i节点为结尾的回文串个数
            mX += coun[i]*qry(N[i].si);
        }
    }
}P;
int main() {
    while (~scanf("%s", P.s)){
        P.mX = 0;
        P.init();
        for (int i = 0; P.s[i]; ++i) P.insert(i);
        P.ans();
        printf("%lld\n", P.mX);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值