[BZOJ3676] [Apio2014]回文串 && Manacher + Hash

本文介绍了一种利用Hash算法解决回文串问题的方法。通过构建树形结构并计算每个节点为中心的最长回文串出现次数,最终通过DFS更新答案来统计所有回文串的出现次数。

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

看到回文串自然会想到manacher 问题在于怎么统计回文串出现的次数 传说有两种做法 一种是Hash 另一种是 后缀数组  这里我用的Hash

对于一个回文字符串A 我们由去掉它两端字符的回文串A'连一条边 这样就形成了一个树形结构

然后把每一个点为中心的最长回文串 (注意 一定是最长回文串 否则会加重复) 的出现次数加1 这样每一个回文串出现的次数就等于以它为根的子树中串出现次数的总和 dfs更新答案即可

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<map>
#define mp make_pair
#define fir first
#define idx(c) (c-'a'+1)
#define sec second
#define SF scanf
#define PF printf
using namespace std;
typedef long long LL;
typedef unsigned int ULL;
typedef pair<ULL, int> pii;
const int MAXN = 1000000;
const int MOD = 10007;
const int car1 = 2333;
const int car2 = 97;
ULL base1[MAXN+10], hash1[MAXN+10];
int base2[MAXN+10], hash2[MAXN+10];
int len[MAXN+10], cnt = 1, f[MAXN+10], fa[MAXN+10], sum[MAXN+10];
map <pii, int> M;
int n, m;
char s[MAXN+10], str[MAXN+10];
LL ans;
struct Node {
    int v, next;
} Edge[MAXN+10];
int adj[MAXN+10], ecnt;
void add(int u, int v) {
    Node &e = Edge[++ecnt];
    e.v = v; e.next = adj[u]; adj[u] = ecnt;
}
void Read_input() {
    SF("%s", str+1); 
    m = strlen(str+1);
    s[0] = '+'; s[++n] = '#';
    for(int i = 1; i <= m; i++) {
        s[++n] = str[i];
        s[++n] = '#';
    }
    s[++n] = '-';
}
void Hash_init() {
    base1[0] = base2[0] = 1;
    for(int i = 1; i <= m; i++) {
        base1[i] = base1[i-1] * car1; base2[i] = base2[i-1] * car2 % MOD;
    }
    ULL RK1 = 0; int RK2 = 0;
    for(int i = 1; i <= m; i++) {
        RK1 = RK1 * car1 + idx(str[i]);
        RK2 = RK2 * car2 + idx(str[i]);
        hash1[i] = RK1; hash2[i] = (RK2 %= MOD);
    }
}
int GetHash(int l, int r) {
    ULL key1 = hash1[r] - hash1[l-1] * base1[r-l+1];
    int key2 = (hash2[r] - hash2[l-1] * base2[r-l+1] % MOD + MOD) % MOD;
    if(l > r) key1 = key2 = 0;
    if(M.count(mp(key1, key2))) return M[mp(key1, key2)];
    len[++cnt] = r-l+1;
    return M[mp(key1, key2)] = cnt;
}
void Manacher_algorithm() {
    f[1] = 1;
    int P = 1;
    M[mp(0, 0)] = 1;
    for(int i = 2; i <= n; i++) {
        int L, R, u, v; 
        if(P + f[P] > i) f[i] = min(f[P] + P - i, f[2*P-i]);
        else f[i] = 1;
        while(s[i-f[i]] == s[i+f[i]]) {
            L = R = u = v = 0;
            f[i]++;
            L = (i - f[i]) / 2 + 1;
            R = (i + f[i]) / 2 - 1;
            u = GetHash(L+1, R-1);
            v = GetHash(L, R);
            if(fa[v] == u) continue;
            add(u, v); fa[v] = u;
        }
        L = R = u = v = 0;
        L = (i - f[i]) / 2 + 1;
        R = (i + f[i]) / 2 - 1;
        v = GetHash(L, R);
        sum[v]++;
        if(f[P] + P < f[i] + i) P = i;
    }
}
void dfs(int u) {
    for(int i = adj[u]; i; i = Edge[i].next) {
        int v = Edge[i].v;
        dfs(v);
        sum[u] += sum[v];
    }
    ans = max(ans, 1LL * sum[u] * len[u]);
}
int main() {
    Read_input();
    Hash_init();
    Manacher_algorithm();
    dfs(1);
    cout << ans;
    return 0;
}


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值