字符串哈希 - # P3501 [POI 2010] ANT-Antisymmetry

题目描述

(https://szkopul.edu.pl/problemset/problem/EDxOyJiFZWb_PYVaYhhnhU0-/site/?key=statement)」

对于一个 \(0/1\) 字符串,如果将这个字符串 \(0\)\(1\) 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串。比如 \(00001111\)\(010101\) 就是反对称的,而 \(1001\) 就不是。
现在给出一个长度为 \(n\)\(0/1\) 字符串,求它有多少个子串是反对称的,注意这里相同的子串出现在不同的位置会被重复计算。

输入格式

第一行一个正整数 \(n\)
第二行一个长度为 \(n\)\(0/1\) 字符串。

输出格式

一行一个整数,表示原串的反对称子串个数。

输入输出样例 #1

输入 #1

8
11001011

输出 #1

7

说明/提示

样例的 \(7\) 个反对称子串分别是:\(01\)(出现两次),\(10\)(出现两次),\(0101\)\(1100\)\(001011\)

对于 \(100\%\) 的数据, \(1\le n\le 500\ 000\)

题解

  • 思考1: 因为“反回文”性质,回文串的长度一定是偶数。

  • 思考2: 判断有多少个子串是回文串。
    对于某个子串,如果前缀hash==后缀hash,该串就是回文子串。枚举区间 \([i,j]\) ,效率 \(o(n^2)\)

  • 思考3: 存在长度为 \(L \times 2\) 的回文串,必然存在一个长度为 \([2,(L-1) \times 2]\) 的反对称回文串。
    满足二分性质, 枚举中心点,可以二分长度 L,\(nlog(n)\)

#include <iostream>
#define LL long long
#define ULL unsigned long long
using namespace std;
const int MAXN = 500010;
const ULL base1 = 131, base2 = 137;
const ULL MOD1 = 1e9 + 7, MOD2 = 1e9 + 9;

int n;
string s;
ULL pow1[MAXN], pow2[MAXN];
ULL pre1[MAXN], pre2[MAXN];       // 原串正向哈希
ULL revPre1[MAXN], revPre2[MAXN]; // 取反并反转后的哈希

void init() {
    pow1[0] = pow2[0] = 1;
    for (int i = 1; i <= n; i++) {
        pow1[i] = pow1[i-1] * base1 % MOD1;
        pow2[i] = pow2[i-1] * base2 % MOD2;
    }
    for (int i = 1; i <= n; i++) {
        pre1[i] = (pre1[i-1] * base1 + (s[i-1] - '0')) % MOD1;
        pre2[i] = (pre2[i-1] * base2 + (s[i-1] - '0')) % MOD2;
    }
    // 构建取反并反转后的哈希, 字符取反,方向取反。
    for (int i = 1; i <= n; i++) {
        int val = (s[n-i] == '0') ? 1 : 0; // 0->1, 1->0
        revPre1[i] = (revPre1[i-1] * base1 + val) % MOD1;
        revPre2[i] = (revPre2[i-1] * base2 + val) % MOD2;
    }
}

ULL getHash(int l, int r) {
    ULL h1 = (pre1[r] - pre1[l-1] * pow1[r-l+1] % MOD1 + MOD1) % MOD1;
    ULL h2 = (pre2[r] - pre2[l-1] * pow2[r-l+1] % MOD2 + MOD2) % MOD2;
    return  h1 * MOD1 + h2;
}

ULL getRevHash(int l, int r) {
    ULL h1 = (revPre1[r] - revPre1[l-1] * pow1[r-l+1] % MOD1 + MOD1) % MOD1;
    ULL h2 = (revPre2[r] - revPre2[l-1] * pow2[r-l+1] % MOD2 + MOD2) % MOD2;
    return  h1 * MOD1 + h2;
}

bool check(int l, int r) {  // 检查原串[l..r]是否反对称
    int len = r - l + 1;
    // 原串[l..r] 对应 取反反转串中的位置: 原l对应取反反转串的n-l+1
    int revL = n - r + 1;
    int revR = n - l + 1;
    return getHash(l, r) == getRevHash(revL, revR);
}

int main() {
    cin >> n >> s;
    init();
    LL ans = 0;
    
    for (int center = 1; center < n; center++) { // 长度为偶数的回文串的中心点
        int l = 0, r = min(center, n - center), best = 0;
        while (l < r) {
            int mid = (l + r + 1) / 2;
            if (check(center - mid + 1, center + mid)) {
                l = mid ;
            } else {
                r = mid - 1;
            }
        }
        ans += l;
    }
    cout << ans << endl;
    return 0;
}
posted @ 2026-06-13 09:51  alice_ss  阅读(1)  评论(0)    收藏  举报
//雪花飘落效果