2084: [Poi2010]Antisymmetry
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 577 Solved: 368
[ 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
HINT
Source
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
const int maxn = 5E5 + 50;
typedef long long LL;
int n,cnt,p,ch[maxn][2],du[maxn],fail[maxn],g[maxn],len[maxn];
char s[maxn];
LL Ans;
queue <int> Q;
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
cin >> n; scanf("%s",s + 1);
for (int i = 1; i <= n; i++) s[i] -= '0';
for (int i = 2; i <= n; i++)
{
if (len[p] == i - 1) p = fail[p];
while (s[i-len[p]-1] == s[i])
{
if (!p) break;
p = fail[p];
}
if (!p)
{
if (s[i] == s[i-1]) continue;
if (!ch[p][s[i]]) ch[p][s[i]] = ++cnt;
p = ch[p][s[i]]; ++g[p]; len[p] = 2;
continue;
}
if (!ch[p][s[i]])
{
int tmp = p; bool flag = 1;
ch[p][s[i]] = ++cnt;
len[cnt] = len[p] + 2; p = fail[p];
while (s[i-len[p]-1] == s[i])
{
if (!p) {flag = 0; break;}
p = fail[p];
}
if (flag)
{
p = ch[p][s[i]];
fail[cnt] = p; ++du[p];
}
p = tmp;
}
p = ch[p][s[i]]; ++g[p];
}
for (int i = 1; i <= cnt; i++)
if (!du[i]) Q.push(i);
while (!Q.empty())
{
int k = Q.front(); Q.pop();
g[fail[k]] += g[k]; --du[fail[k]];
if (!fail[k] || du[fail[k]]) continue;
Q.push(fail[k]);
}
for (int i = 1; i <= cnt; i++) Ans += 1LL*g[i];
cout << Ans;
return 0;
}