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; }