您的位置:首页 > 其它

【BZOJ】【2084】【POI2010】Antisymmetry

2015-04-07 17:15 260 查看

Manacher算法

  啊……Manacher修改一下就好啦~蛮水的……

  Manacher原本是找首尾相同的子串,即回文串,我们这里是要找对应位置不同的“反回文串”(反对称?233)

  长度为奇数的肯定不满足>_>(中间那个字符无论如何不反对称)

  那么我们就找'#'为中心的即可……

  将判断条件a[i-p[i]-1]==a[i+p[i]+1]改成【不等……或是两个都是'#'】

  将所有的p[i]加起来,即所有“反回文串”的长度加起来除以二就是答案啦~

  看代码吧>_<

/**************************************************************
Problem: 2084
User: Tunix
Language: C++
Result: Accepted
Time:68 ms
Memory:9572 kb
****************************************************************/

//BZOJ 2084
#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
#define pb push_back
using namespace std;
typedef long long LL;
inline int getint(){
int r=1,v=0; char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-')r=-1;
for(; isdigit(ch);ch=getchar()) v=v*10+ch-'0';
return r*v;
}
const int N=5e5+10,INF=~0u>>2;
/*******************template********************/
char s
;
int a[N<<1],p[N<<1];
int main(){
int n=getint();
scanf("%s",s);
F(i,1,n) a[i<<1]=s[i-1];
n=n<<1|1;
int id=0,ans=0;
F(i,1,n){
if (a[i]!=0) continue;
if (p[id]+id>i) p[i]=min(p[2*id-i],p[id]+id-i);
else p[i]=0;
while(i-p[i]-1>0 && i+p[i]+1<=n &&
(a[i-p[i]-1]!=a[i+p[i]+1] || a[i-p[i]-1]==0))p[i]++;
if (p[i]+i>p[id]+id) id=i;
ans+=p[i];
}
printf("%d\n",ans/2);
return 0;
}


View Code

2084: [Poi2010]Antisymmetry

Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 247 Solved: 162
[Submit][Status][Discuss]

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

[Submit][Status][Discuss]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: