BZOJ2084[Poi2010] Antisymmetry

来源:互联网 发布:unity3d lua 开发 编辑:程序博客网 时间:2024/05/20 05:31

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,R1],[L+2,R2],[L+3,R3]等等都是反对称子串。

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

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

#include<stdio.h>#include<iostream>#define P 1000000007#define B 200019#define M 500005#define ll long longusing 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;}
0 0