2019 徐州网络赛 G Colorful String(回文树)

链接

题意:

给出一个串,对于每个回文串算出有几种字母,然后所有的回文串求和。



思路:

利用回文树处理本质相同的回文串计数,对于一种本质的回文串,利用一个右端点和长度,可以找出这种本质具体长什么样子,利用对26个字母做前缀和,O26判断一种本质的回文串有几种字母。(也可以用马拉车做)



代码:
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int MAXN = 3e5 + 5;
const int N = 26;

ll pre[MAXN][26];

struct Palindromic_Tree {
    ll next[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    ll fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点
    ll cnt[MAXN];//cnt[i]表示节点i表示的本质不同的串的个数,需跑count函数
    ll num[MAXN];//表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
    ll len[MAXN];//len[i]表示节点i表示的回文串的长度
    ll S[MAXN];//存放添加的字符
    ll last;//指向上一个字符所在的节点,方便下一次add
    ll n;//字符数组指针
    ll p;//节点指针
    ll pos[MAXN];//某种本质的回文串的一个右端点

    ll newnode(ll l) {//新建节点
        for (ll i = 0; i < N; ++i) next[p][i] = 0;
        cnt[p] = 0;
        num[p] = 0;
        len[p] = l;
        return p++;
    }

    void init() {
        p = 0;
        newnode(0);
        newnode(-1);
        last = 0;
        n = 0;
        S[n] = -1;
        fail[0] = 1;
    }

    ll get_fail(ll x) {
        while (S[n - len[x] - 1] != S[n]) x = fail[x];
        return x;
    }

    void add(ll c) {
        c -= 'a';
        S[++n] = c;

        ll cur = get_fail(last);
        if (!next[cur][c]) {
            ll now = newnode(len[cur] + 2);
            fail[now] = next[get_fail(fail[cur])][c];
            next[cur][c] = now;
            num[now] = num[fail[now]] + 1;
        }

        last = next[cur][c];
        cnt[last]++;
        pos[last] = n;
    }

    void count() {
        for (ll i = p - 1; i >= 0; --i) {
            cnt[fail[i]] += cnt[i];
        }
    }

    ll get_ans() {
        ll ret = 0;
        for (ll i = 2; i < p; i++) {
            ll r = pos[i];
            ll l = r - len[i] + 1;
//            printf("cnt:%lld l:%lld r:%lld\n", cnt[i], l, r);
            ll sum = 0;
            for (ll j = 0; j < 26; j++) {
                if (pre[r][j] - pre[l - 1][j]) {
                    sum++;
                }
            }
            ret += sum * cnt[i];
        }
        return ret;
    }
} pam;

char ss[MAXN];


int main() {
    pam.init();
    scanf("%s", ss);
    ll len = strlen(ss);
    for (ll i = 1; i <= len; i++) {
        for (ll j = 0; j < 26; j++) {
            if (j == ss[i - 1] - 'a') {
                pre[i][j] = pre[i - 1][j] + 1;
            } else {
                pre[i][j] = pre[i - 1][j];
            }
        }
    }
    for (ll i = 0; i < len; i++) {
        pam.add(ss[i]);
    }
    pam.count();
    printf("%lld\n", pam.get_ans());
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值