ABC295 D题 题解
题意简述
给定一个长度不超过\(\;5\times 10^5\;\)的,仅有数字构成的字符串,问存在多少段子串,使得子串内字符重新排序后,前半段与后半段相同?
做法分析
重组后前后两部分相同,其实也就意味着,这一子串内所有数字出现的次数都为偶数次。
考虑暴力做法,枚举左端点和右端点,统计子串内每个数字出现次数,判断是否都为偶次。但是这样达到\(O(n^3)\)级别,效率过低。
可以利用前缀和的思想进行优化,对于每一位 i,统计从第一位到这一位每个数字 x 出现的次数 \(num_{i,x}\) ,而子串 \((L, R)\) 内数字 x 出现次数为偶次,等价于 \(num_{R, x}\) 减 \(num_{L-1, x}\) 为偶次,等价于 \(num_{R, x}\) 和 \(num_{L-1, x}\) 的奇偶性相同。
这时可以发现,其实出现了几次并不重要,重要的是出现次数的奇偶性,那就可以把每一位的状态值整合成一个二进制下,十位的,由 0 和 1 组成的数字。而出现的每个数字奇偶性相同等价于状态值相同。
那做法就很明了了,从前往后统计每一位的状态值,将答案加上这一状态值之前出现的次数,最后在将这一状态值出现次数加 1 。
参考代码
#include <bits/stdc++.h>
using namespace std;
string s;
long long ans, now;
map<long long, int> m;
int main()
{
cin >> s;
m[0] = 1; //注意:第零位,每种数字都没出现也要计算,因为从第一位到第 x 位实际上是要判断第 0 位和第 x 位的状态值
for(int i = 0;i < s.size(); i++)
{
int x = s[i] - '0';
now ^= 1 << x;
ans += m[now];
m[now]++;
}
cout << ans;
return 0;
}