问题 H: 【哈希和哈希表】Antisymmetry
时间限制: 1 Sec 内存限制: 128 MB提交: 13 解决: 3
[提交] [状态] [讨论版] [命题人:admin]
题目描述
对于一个0/1字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作「反对称」字符串。比如00001111和010101就是反对称的,而1001就不是。
现在给出一个长度为n的0/1字符串,求它有多少个子串是反对称的,注意这里相同的子串出现在不同的位置会被重复计算。
现在给出一个长度为n的0/1字符串,求它有多少个子串是反对称的,注意这里相同的子串出现在不同的位置会被重复计算。
输入
第一行一个正整数n。
第二行一个长度为n的0/1字符串。
第二行一个长度为n的0/1字符串。
输出
一行一个整数,表示原串的反对称子串个数。
样例输入
8
11001011
样例输出
7
提示
对于100%的数据,1≤n≤500000。
马拉车裸题。
马拉车算法:https://www.cnblogs.com/grandyang/p/4475985.html
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e6+10; int p[maxn],n,len; ll ans; char s[maxn],ms[maxn]; bool check(char x,char y){ if(((x-'0')^(y-'0'))==1) return 1; if(x=='#' && y=='#') return 1; return 0; } void init(char *s){ ms[0]='$',ms[1]='#'; for (int i=0; s[i]; i++){ ms[i*2+2]=s[i]; ms[i*2+3]='#'; } len=strlen(s)*2+2; ms[len]='$'; } inline ll calc(){ ans=0; for (int i=1; i<len; i+=2) ans+=1ll*(p[i]>>1); return ans; } void manacher(char *s){ init(s); int mx=0,id=0; for (int i=1; i<len; i+=2){ p[i]=(mx>i)?min(p[2*id-i],mx-i):1; while(check(ms[i+p[i]],ms[i-p[i]])) p[i]++; if(mx<i+p[i]){ mx=i+p[i]; id=i; } } printf("%lld\n",calc()); } int main(){ scanf("%d%s",&n,s); manacher(s); return 0; }