P7114 [NOIP2020] 字符串匹配
代码先行
#include <bits/stdc++.h>
using namespace std;
const int maxx = (1 << 21);
string s;
int pos[maxx][26],pre[maxx][26],cpos[maxx],cpre[maxx];
int cnt[maxx];//辅助统计当前奇数个数为i的个数
int temp,postodd,temp2,postodd2;
int z[maxx];
int T,len;
int l,r;
int i,k;
int n1,n2;
long long ans;
void exkmp()
{
l = r = 0;
z[0] = 0;
for (i = 1; i < len; i++)
{
if(r >= i && z[i - l] < r - i + 1)
{
z[i] = z[i - l];
}
else{
z[i] = max(0,r - i + 1);
while( i + z[i] < len && s[ z[i] ] == s[ i + z[i] ] ) z[i]++;
}
if(i + z[i] > l + z[l]) l = i,r = i + z[i] - 1;
//cout << z[i] << " ";
}
z[0] = len;
}
void prep()
{
memset(pre,0,sizeof(pre));
memset(pos,0,sizeof(pos));
pre[0][ s[0] - 'a' ] ^= 1;
if(pre[0][ s[0] - 'a' ] == 0)
{
cpre[0] = 0;
}
else cpre[0] = 1;
for (i = 1; i < len; i++)
{
memcpy(pre[i],pre[i - 1],sizeof(pre[i]));
pre[i][ s[i] - 'a' ] ^= 1;
if(pre[i][ s[i] - 'a' ] == 0)
{
cpre[i] = cpre[i - 1] - 1;
}
else cpre[i] = cpre[i - 1] + 1;
}
pos[len - 1][ s[len - 1] - 'a' ] ^= 1;
if(pos[len - 1][ s[len - 1] - 'a' ] == 0)
{
cpos[len - 1] = 0;
}
else cpos[len - 1] = 1;
for (i = len - 2; i >= 0; i--)
{
memcpy(pos[i],pos[i + 1],sizeof(pos[i]));
pos[i][ s[i] - 'a' ] ^= 1;
if(pos[i][ s[i] - 'a' ] == 0)
{
cpos[i] = cpos[i + 1] - 1;
}
else cpos[i] = cpos[i + 1] + 1;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> T;
while(T--)
{
memset(cnt,0,sizeof(cnt));
ans = 0;
cin >> s;
len = s.length();
exkmp();
prep();
postodd = cpos[0];
postodd2 = cpos[0];
temp = 0;
temp2 = 0;
for (i = 1; i < len; i++)//枚举AB串长度
{
k = min(z[i],len - 1 - i) / i + 1;//最大的重复次
n1 = (k + 1) >> 1,n2 = (k) >> 1;
//处理k为奇数的情况
if( pos[i - 1][ s[i - 1] - 'a' ] == 1 )//下一步会变成奇数,加入前缀
{
temp -= cnt[postodd];//减去前面所有至结尾为止个数为奇数的个数
postodd--;
}
else
{
postodd++;
temp += cnt[postodd];
}
ans += 1ll * temp * n1;//所有为奇数的
cnt[ cpre[i - 1] ]++;
if(cpre[i - 1] <= postodd) temp++;//B串不能为空串,所以A的末尾必须小与i - 1,所以ans更新在前面,在这里错了好久!!!!!
/*if(pos[i - 1][ s[i - 1] - 'a' ] == 1)//减去一个奇数
{
temp2 -= cnt[postodd2];
postodd2--;
}
else//减去一个偶数
{
postodd2++;
temp2 += cnt[postodd2];
}
if(cpre[i - 1] <= postodd2) temp2++;*/
//辅助处理k为偶数的情况,等价于与整个串的奇偶性的匹配
ans += 1ll * temp2 * n2;//所有为偶数的
if(cpre[i - 1] <= cpre[len - 1])temp2++;
}
cout << ans << endl;
}
return 0;
}
思路:
分析题目,得到目标:枚举AB长度,找到AB串中满足条件的A的个数,注意此时B不为空
1.用exkmp去判断循环次数
2.注意到题目中重要的是奇偶性,所以只考虑循环次数的奇偶性,由于 k <= Zi / i+1 并且C串不为空,所以ik <= n - 1,得出k范围,分奇偶(n1,n2)
3.优化复杂度,由于postodd每次只变化1,那么我们记录一下,从i变化到i + 1或者i - 1时,A串满足条件的个数的变化情况,奇数k没什么好说的,偶数k:我们注意到偶数k的时候,C串的奇偶性与整串一致,于是直接用整串的奇偶性即可。
有一个很坑的点,再exkmp里,我先把z[0] 初始化为 len, 然后导致 l + z[l] 很大,l一直不更新,和暴力同分。