LOJ 2452 对称 Antisymmetry——用hash求回文串数

概念

用hash求最长回文串/回文串数

首先,易知,回文串具有单调性。

如果字符串 $s[l...r]$ 为回文串串,那么 $s[x...y]$($l < x, y < r$ 且 $|l-x| = |r-y|$)也一定是回文串。

因此,可以二分。

通常,枚举一下起点或者中点,然后二分长度。

这样复杂度为 $O(nlogn)$,逊色于马拉车 $O(n)$,但在时限不那么紧的情况下,hash也是不错的选择。

例题

题意:对于一个 0/1 串,如果将这个字符串 0 和 1 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串。先给出一个长为 $n$ 的 0/1 串,求它有多少反对称字串。($1 \leq n\leq 500000$)

分析:

先正向求一遍hash,再反向求一遍hash(0,1互换)。

枚举中点,判断左右两半的hash值是否相同。

显然,不存在长度为奇数的反对称串。

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
typedef long long ll;
const ull base = 233;
const int maxn = 5e5 + 10;

ull h1[maxn], h2[maxn], p[maxn];
int n;
ll ans;
char s[maxn];

ull get_h1(int l, int r)
{
    return h1[r] - h1[l-1]*p[r-l+1];
}
ull get_h2(int l, int r)
{
    return h2[l] - h2[r+1]*p[r-l+1];
}

int check(int x)
{
    int l=1, r = min(x, n-x);
    int ans=0;
    while(l <= r)
    {
        int mid = (l+r) >> 1;
        if(get_h1(x-mid+1,x) == get_h2(x+1, x+mid))
        {
            ans = mid;
            l = mid + 1;
        }
        else r = mid - 1;
    }
    return ans;
}

int main()
{
    scanf("%d%s", &n, s+1);
    p[0] = 1;
    for(int i = 1;i <= n;i++)
    {
        p[i] = p[i-1]*base;
        h1[i] = h1[i-1]*base + (ull)s[i];
    }
    for(int i = n;i >= 1;i--)  h2[i] = h2[i+1]*base + (ull)(s[i] == '0' ? s[i]+1 : s[i]-1);

    for(int i = 1;i < n;i++)  ans += check(i);
    printf("%lld\n", ans);
    return 0;
}

 

 

参考链接:https://www.cnblogs.com/henry-1202/p/10321013.html

 

posted @ 2019-08-19 10:06  Rogn  阅读(392)  评论(0编辑  收藏  举报