您的位置:首页 > 其它

BZOJ2084[Poi2010] Antisymmetry

2016-09-26 18:58 393 查看
BZOJ2084[Poi2010] Antisymmetry

Description

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

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

Input

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

Output

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

Sample Input

8

11001011

Sample Output

7

HINT

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

Solution:

挺不错的一道题..(其实是因为你自己YY出来了吧)

最开始想枚举一点,计算另一点,然后就怎么也弄不出来。后来可以发现一些性质:

反对称子串长度一定是偶数(这不废话吗)

反对称子串的中心串一定是反对称子串:设原串的区间为[L,R],则[L+1,R−1],[L+2,R−2],[L+3,R−3]等等都是反对称子串。

一旦发现了这第二个性质,就非常简单了。枚举中心点,二分答案最长的以此为中心的反对称子串,哈希一下就好了。复杂度O(nlogn)。

话说这题好像用Manacher算法可以做到O(n),以后再说吧。

#include<stdio.h>
#include<iostream>
#define P 1000000007
#define B 200019
#define M 500005
#define ll long long
using namespace std;
int Hash1[M],Hash2[M],Base[M],n;
char str[M],str1[M];
bool check(int i,int j){
int t1=((Hash1[j]-1LL*Hash1[i]*Base[j-i])%P+P)%P;
int t2=((Hash2[n-i+1]-1LL*Hash2[n-j+1]*Base[j-i])%P+P)%P;
return t1==t2;
}
void swap(char &a,char &b){char t=a;a=b;b=t;}
int min(int a,int b){return a<b?a:b;}
int main(){
ll ans=0;
scanf("%d",&n);
scanf("%s",str+1);
Base[0]=1;
for(int i=1;i<=n;i++)
Base[i]=1LL*Base[i-1]*B%P;
for(int i=1;i<=n;i++)
Hash1[i]=(1LL*Hash1[i-1]*B+str[i])%P;
for(int i=1;i<=n;i++)
str1[i]=(str[i]=='0'?'1':'0');
int i1=1,j1=n;
while(i1<=j1)swap(str1[i1++],str1[j1--]);
for(int i=1;i<=n;i++)
Hash2[i]=(1LL*Hash2[i-1]*B+str1[i])%P;
for(int i=1;i<n;i++){
int j=i+1;
if(str[i]==str[i+1])continue;
int L=0,R=min(i-1,n-i-1),res=L;
while(L<=R){
int mid=(L+R)>>1;
if(check(i-mid,i+1+mid)){
L=mid+1;
res=mid;
}else R=mid-1;
}
ans+=res+1;
}
cout<<ans<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: