Description
对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。
现在给出一个长度为
Input
第一行一个正整数
Output
一个正整数,表示反对称子串的个数。
Sample Input
8
11001011
Sample Output
7
hint
7个反对称子串分别是:01(出现两次), 10(出现两次), 0101, 1100和001011
HINT
Source
鸣谢 JZP
思路
看起来像回文串……没错,就是回文串,但是这里的回文串是两边相加为1,也可以用manacher算法解决,但是中间的插入的字符应该怎么解决呢?如果把原串的1换成2,0还是0,中间插入1,寻找相加为2的字符就可以了。寻找答案时扫一遍回文串长度数组就可以了。
注意一些地方:
1. 并不是所有的回文串都是符合对称条件的,只有在中心为1时才可以进行计算。例如21010这个串如果计算了中间的0,那么在统计右侧的1时就会将010视为是满足条件的串(实际上并不满足),就会WA;
2. 统计答案时也只需要统计以1为中心的串。
代码
#include <cstdio>
const int maxn=500000;
char s[maxn+10];
int n,a[(maxn<<1)+10],p[(maxn<<1)+10],id,rmax;
long long ans;
int main()
{
scanf("%d%s",&n,s+1);
a[0]=-3;
a[1]=1;
for(register int i=1; i<=n; ++i)
{
if(s[i]-'0')
{
a[i<<1]=2;
}
else
{
a[i<<1]=0;
}
a[i<<1|1]=1;
}
n=n<<1|1;
a[n+1]=-3;
p[1]=1;
id=1;
rmax=1;
for(register int i=3; i<=n; i+=2)
{
if(i>rmax)
{
p[i]=1;
}
else
{
if(p[(id<<1)-i]<rmax-i)
{
p[i]=p[(id<<1)-i];
}
else
{
p[i]=rmax-i;
}
}
while(a[i+p[i]]+a[i-p[i]]==2)
{
++p[i];
}
if(i+p[i]-1>rmax)
{
rmax=i+p[i]-1;
id=i;
}
}
ans=0;
for(register int i=1; i<=n; i+=2)
{
ans+=p[i]>>1;
}
printf("%I64d\n",ans);
return 0;
}