[BZOJ2084][Poi2010]Antisymmetry

[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;
}
posted @ 2017-10-24 20:13  xjr01  阅读(142)  评论(0编辑  收藏  举报