Problem F Palindromadness

该博客探讨了一种简单的题目,寻找两个都是回文串且其中一个为另一个子串的字符串A和B。通过回文树的性质,可以确定存在一条路径从B到其父节点,然后fail指针直接指向A。重点在于如何处理fail指针指向A的多个子节点,以避免在计算f[x](A长度为x的AB对数)时重复计数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单题意, 找两个字符串, A B, 

A, B 都是回文串, A 是 B 的子串,

A 和 B 的位置任意,可以相同,

f[x] 为 A 的长度为 x , 的 A B 的对数.

 

根据回文树的性质,

如果A 是B 的子串,

那么一定会有一条路径, B 找它的父亲,然后直接 fail 指向 A.

所以我们只要考虑, A 的子树, fail 指针指向 A 的那些节点的子树.

 

有可能有多个节点的fail 指向同一个 节点,

多个节点有可能会算重复,这个就需要我们判断重复了. 

 

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+100;
char s[N];
int n,base,mod;
long long f[N];
namespace PAM {
    int sz, fl[N], len[N], ch[N][26],cnt[N],vis[N],l[N];
    char *s;
    int find(int x, int y) {
        return s[y] == s[y - len[x] - 1] ? x : find(fl[x], y);
    }
    void cal(char *str) {
        s = str;
        int n = strlen(s + 1);
        for (int i = 0; i <= n + 2; ++i){
            fl[i] = cnt[i] = 0;
            for (int  j = 0; j < 26; ++j)
                ch[i][j] = 0;
        }
        int now = 0;
        sz = 1;
        fl[0] = 1;   
        len[0] = 0;   len[1] = -1;
        for (int i = 1; i <= n; i++) {
            now = find(now, i);
            if (!ch[now][s[i] - 'a']) {
                len[++sz] = len[now] + 2;
                fl[sz] = ch[find(fl[now], i)][s[i] - 'a'];
                ch[now][s[i] - 'a'] = sz;
            }
            now = ch[now][s[i] - 'a'];
            cnt[now]++;
            l[now] = i;
        }
        for (int i = sz; i > 1; --i){
            cnt[fl[i]] += cnt[i];
        }
    }
    long long size[N],num[N];
    void dfs1(int u){
        num[u] = cnt[u];
        for (int i = 0; i < 26; ++i){
            if (ch[u][i]) dfs1(ch[u][i]), num[u] = (num[u] + num[ch[u][i]] ) % mod;
        }
    }
    void dfs(int u){
        size[u] = (size[u] + num[u] * cnt[u]) % mod;    
        if (vis[fl[u]] == 0) size[fl[u]] = (size[fl[u]] + 1ll*num[u] * cnt[fl[u]]) % mod;
        vis[fl[u]]++; vis[u]++;
        for(int i = 0; i < 26; ++i){
            if (ch[u][i]) dfs(ch[u][i]);
        }
        vis[fl[u]]--; vis[u]--;
    }
    long long solve(){
        for (int i = 0; i <= sz; ++i) size[i] = 0;
        long long ans = 0;
        dfs1(1); dfs1(0);
        dfs(1); dfs(0);
        for (int i = 2; i <= sz; ++i){
            ans = (ans + 1ll*size[i]*f[n-len[i]]) % mod;
        }
        return ans;
    }
};
int main(){
    int T;
    scanf("%d",&T);
    for (int cas = 1; cas <= T; ++cas){
        scanf("%d%d%d",&n,&base,&mod);
        f[0] = 1;
        for (int i = 1; i <= n; ++i)
            f[i] = (f[i-1]*base) % mod;
        scanf("%s",s+1); 
        PAM::cal(s);
        printf("Case %d: %lld\n",cas,PAM::solve());
    }
    return 0;
}

/*
5
13 100 999  
welcometomist
7 1000 1000000000
topspot
11 23167 21898192
abbabobaxab
21 123456 123456789
amanaplanacanalpanama
32 72817 728917897
bobxyxthehtxyxbuildercantfixyxit
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值