https://loj.ac/problem/2452
题目描述
反对称串定义为进行\(0、1\)取反后再反过来和原串相同的字符串,给出一个字符串,求它的多少个子串是反对称串。
思路
首先我们从反对称串的定义入手,我们考虑如果一个串\(S\)为反对称串,显然它的长度是偶数,那么它也就是以它的对称轴为分割后将其前一部分反转后对称的字符串。因此,转化定义后我们的字符串具有单调性,即若一个字符串为反对称串,那么我们删除首尾后它仍是反对称串。所以我们可以暴力枚举每一个点为对称轴,二分查找其为反对称串的位置,对答案\(+\)找到最大反对称串长度的一半,二反对称串的判定可以用字符串\(Hash\)实现\(O(1)\)判断。
代码
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const ull p=47;
char s[500005];
ull power[500005],sum1[500005],sum2[500005];
ull f_hash(ull k,ull n,bool f)
{
if(f)return sum1[k]-sum1[k-n]*power[n];
else return sum2[k]-sum2[k+n]*power[n];
}
ull check(ull pos,ull len)
{
if(s[pos]==s[pos+1])return 0;
ull l=1,r=len;
while(l<=r)
{
ull mid=l+r>>1;
if(f_hash(pos,mid,1)==f_hash(pos+1,mid,0))l=mid+1;
else r=mid-1;
}
return r;
}
int main()
{
int n;
scanf("%d",&n);
scanf(" %s",s+1);
power[0]=1;
for(int i=1;i<=n;i++)
{
sum1[i]=sum1[i-1]*p+s[i];
power[i]=power[i-1]*p;
}
for(int i=n;i>=1;i--)
sum2[i]=sum2[i+1]*p+('1'-s[i]+'0');
// for(int i=1;i<=n;i++)
// printf("%llu %llu %llu\n",sum1[i],sum2[i],power[i]);
ull ans=0;
for(ull i=1;i<n;i++)
ans+=check(i,min(i,n-i));
printf("%llu",ans);
return 0;
}