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

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