P3501 [POI2010]ANT-Antisymmetry

P3501 [POI2010]ANT-Antisymmetry

二分+hash

注意:答案超出int范围


------------

先拿一个反对称串来做栗子:010101

我们可以发现 0101(左边右边各削掉1个),01(左边右边各削掉2个)都是反对称串

多举几个例子,我们可以总结出一个性质:一个反对称串的所有同中心的子串都是反对称串

∴长为 n 的子串中的反对称子串数= n/2 

------------
于是我们就可以设计算法了

每次枚举中心点,然后二分查找反对称串的最长长度。

对于反对称的问题,我们可以将主串正序逆序都计算一遍hash值。为了方便逆序的可以直接取反

每次判断时将子串取出,就可以达O(1)判断


复杂度O(nlogn)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
inline int min(int &a,int &b) {return a<b ?a:b;}
const int base=19260817;
char q[500002]; int n;
ull h1[500002],h2[500002],fac[500002],ans; //自然溢出hash
int main(){
    scanf("%d",&n); fac[0]=1;
    scanf("%s",q);
    for(int i=1;i<=n;++i){
        h1[i]=h1[i-1]*base+(q[i-1]=='1');
        fac[i]=fac[i-1]*base; //通用取串方法:利用fac数组取出子串hash值,其中fac[i]=base^i
    }
    for(int i=n;i>=1;--i) h2[i]=h2[i+1]*base+(q[i-1]=='0'); //逆序计算(直接取反)
    for(int i=1;i<n;++i){
        int l=0,r=min(i,n-i),mid;
        while(l<r){ //二分查找长度的一半
          mid=l+((r-l)>>1)+1;
          ull p1=h1[i+mid]-h1[i-mid]*fac[mid*2];
          ull p2=h2[i-mid+1]-h2[i+mid+1]*fac[mid*2]; //取出子串
          if(p1==p2) l=mid;
          else r=mid-1;
        }
        ans+=l;
    }cout<<ans;
    return 0;
}

 

posted @ 2018-09-07 16:57  kafuuchino  阅读(175)  评论(0编辑  收藏  举报