【BZOJ】【2084】【POI2010】Antisymmetry
Manacher算法
啊……Manacher修改一下就好啦~蛮水的……
Manacher原本是找首尾相同的子串,即回文串,我们这里是要找对应位置不同的“反回文串”(反对称?233)
长度为奇数的肯定不满足>_>(中间那个字符无论如何不反对称)
那么我们就找'#'为中心的即可……
将判断条件a[i-p[i]-1]==a[i+p[i]+1]改成【不等……或是两个都是'#'】
将所有的p[i]加起来,即所有“反回文串”的长度加起来除以二就是答案啦~
看代码吧>_<
1 /************************************************************** 2 Problem: 2084 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:68 ms 7 Memory:9572 kb 8 ****************************************************************/ 9 10 //BZOJ 2084 11 #include<vector> 12 #include<cstdio> 13 #include<cstring> 14 #include<cstdlib> 15 #include<iostream> 16 #include<algorithm> 17 #define rep(i,n) for(int i=0;i<n;++i) 18 #define F(i,j,n) for(int i=j;i<=n;++i) 19 #define D(i,j,n) for(int i=j;i>=n;--i) 20 #define pb push_back 21 using namespace std; 22 typedef long long LL; 23 inline int getint(){ 24 int r=1,v=0; char ch=getchar(); 25 for(;!isdigit(ch);ch=getchar()) if(ch=='-')r=-1; 26 for(; isdigit(ch);ch=getchar()) v=v*10+ch-'0'; 27 return r*v; 28 } 29 const int N=5e5+10,INF=~0u>>2; 30 /*******************template********************/ 31 char s[N]; 32 int a[N<<1],p[N<<1]; 33 int main(){ 34 int n=getint(); 35 scanf("%s",s); 36 F(i,1,n) a[i<<1]=s[i-1]; 37 n=n<<1|1; 38 int id=0,ans=0; 39 F(i,1,n){ 40 if (a[i]!=0) continue; 41 if (p[id]+id>i) p[i]=min(p[2*id-i],p[id]+id-i); 42 else p[i]=0; 43 while(i-p[i]-1>0 && i+p[i]+1<=n && 44 (a[i-p[i]-1]!=a[i+p[i]+1] || a[i-p[i]-1]==0))p[i]++; 45 if (p[i]+i>p[id]+id) id=i; 46 ans+=p[i]; 47 } 48 printf("%d\n",ans/2); 49 return 0; 50 }
2084: [Poi2010]Antisymmetry
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 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
11001011
Sample Output
7
hint
7个反对称子串分别是:01(出现两次), 10(出现两次), 0101, 1100和001011
hint
7个反对称子串分别是:01(出现两次), 10(出现两次), 0101, 1100和001011