字符串哈希 - # CF7D Palindrome Degree

题目描述

长度为 \(n\) 的字符串 \(s\) 被称为 \(k\)-回文串,当且仅当它本身是回文串,并且它长度为 的前缀和后缀均为 \((k-1)\)-回文串。根据定义,任何字符串(包括空串)都是 \(0\)-回文串。

我们称字符串 \(s\) 的回文度为,使得 \(s\)\(k\)-回文串的最大整数 \(k\)。例如,字符串 "abaaba" 的回文度为 \(3\)

现给定一个字符串。你的任务是计算其所有前缀的回文度之和。

输入格式

输入数据的第一行为一个非空字符串,仅包含拉丁字母和数字,字符串长度不超过 \(5 \cdot 10^{6}\)。字符串区分大小写。

输出格式

输出一个整数,表示该字符串所有前缀的回文度之和。

输入输出样例 #1

输入 #1

a2A

输出 #1

1

输入输出样例 #2

输入 #2

abacaba

输出 #2

6

题解

  • 思考1: 具有dp性质, 求前缀的回文度 \(dp[i] = dp[i//2] + 1\) ,前缀i表达式为回文串时。
  • 思考2: 需要实现前缀表达式是否为回文串 。
  • 哈希技巧:同时维护正向哈希(左→右)和反向哈希(右→左,但权值从低到高),二者相等即回文。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e6 + 5;
const int P1 = 31, MOD1 = 1e9 + 7;
const int P2 = 131, MOD2 = 1e9 + 9;

char s[N];
int dp[N];
ll pow1[N], pow2[N];

int main() {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    
    // 预处理幂
    pow1[0] = pow2[0] = 1;
    for (int i = 1; i <= n; ++i) {
        pow1[i] = pow1[i - 1] * P1 % MOD1;
        pow2[i] = pow2[i - 1] * P2 % MOD2;
    }
    
    ll l1 = 0, l2 = 0;   // 正向哈希
    ll r1 = 0, r2 = 0;   // 反向哈希(从右向左扩展)
    ll ans = 0;
    
    for (int i = 1; i <= n; ++i) {
        // 更新正向哈希
        l1 = (l1 * P1 + s[i]) % MOD1;  // L[i] = L[i-1] * p + s[i]
        l2 = (l2 * P2 + s[i]) % MOD2;
        // 更新反向哈希(注意权值左高右低)
        r1 = (r1 + s[i] * pow1[i - 1]) % MOD1; // R[i] = R[i-1] +s[i] * pow[i-1]
        r2 = (r2 + s[i] * pow2[i - 1]) % MOD2;
        
        // 双哈希同时相等才判定回文
        if (l1 == r1 && l2 == r2) 
            dp[i] = dp[i >> 1] + 1 , ans += dp[i];
    }
    
    printf("%lld\n", ans);
    return 0;
}
posted @ 2026-06-11 15:44  alice_ss  阅读(4)  评论(0)    收藏  举报
//雪花飘落效果