[BZOJ2084][Poi2010]Antisymmetry

本文介绍了一种通过Manacher算法解决反对称字符串子串计数问题的方法。具体而言,通过将原字符串进行特定转换,使得反对称字符串的问题转化为寻找回文串的问题,进而利用Manacher算法高效求解。

[BZOJ2084][Poi2010]Antisymmetry

试题描述

对于一个 01 字符串,如果将这个字符串 01 取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如 00001111010101 就是反对称的,1001 就不是。

现在给出一个长度为 \(N\)01 字符串,求它有多少个子串是反对称的。

输入

第一行一个正整数 \(N(N \le 500,000)\)。第二行一个长度为N的01字符串。

输出

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

输入示例
8
11001011
输出示例
7
数据规模及约定

见“输入

题解

将原串异或上 101010... 然后直接上 manacher。

我们不难证明“一个01串反对称”等价于“一个01串异或上 101010... 后是回文串”等价(提示:这样的串必须是偶数位,那么从两边往中间每一位一定异或的是不同的数)。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 500010

int n, alp[maxn<<1], len[maxn<<1];
char str[maxn], S[maxn<<1];

int main() {
    n = read();
    scanf("%s", str + 1);
    
    rep(i, 1, n) S[(i<<1)-1] = str[i] - '0' ^ (i & 1), S[i<<1] = '#';
    n <<= 1;
    rep(i, 1, n) alp[i] = alp[i-1] + (0 <= S[i] && S[i] <= 1);
    len[1] = 1;
    int mxi = 1;
    rep(i, 2, n) {
        len[i] = 1;
        if(mxi + len[mxi] - 1 >= i) len[i] = min(max(1, len[(mxi<<1)-i]), mxi + len[mxi] - i);
        while(i - len[i] > 0 && i + len[i] <= n && S[i-len[i]] == S[i+len[i]]) len[i]++;
        if(mxi + len[mxi] < i + len[i]) mxi = i;
    }
    
    int ans = 0;
    rep(i, 1, n) if(S[i] == '#')
        ans += alp[i] - alp[i-len[i]];
    printf("%d\n", ans);
    
    return 0;
}

转载于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7725509.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值