1题意
定义个字符串S是偶串当且仅当S中的每一种字符都出现了偶数次。例如S=‘‘abbacc′′就是一个偶串,因为abc三种字符都出现了偶数次。
题目为输入一个字符串,问S有多少个子串是偶串。
对于任意的,我们使用符号[L,R]表示子串。例如输入的字符串S=‘‘aabbaa′′,它有[1,2],[3,4],[5,6],[1,4],[2,5],[3,6],[1,6]这7个子串是偶串。
2解法
本题有一个显而易见的O(n^3)的解法。枚举子串的左右端点L, R,再检查子串[L, R]是否为一个偶串,如果是,答案加1。最后输出总的偶串个数。这种做法虽然能给出正确答案,但是复杂度太高,大数据会超时。
上述算法可以优化成一个O(n^2)的算法。当我们枚举了左右端点L, R之后,有没有方法可以O(1)的检查子串[L, R]是否为一个偶串?注意到英文字母一共只有26个,我们要求出这26个字符在[L, R]中分别出现了多少次。为了方便表示,我们把字母a对应到数字0,字母b对应到数字1,依次类推字母z对应到数字25。对于
,定义f(i, j)为在
中,数字j对应的字母出现了多少次。例如S=‘‘aabbaa′′时,f(3,0) = 2,表示‘‘aab′′中a出现了3次。
用如下方法计算f(i, j):
这样,[L, R]中字母a出现的次数就是f(R,0)-f(L-1,0),依次检查26个字母的出现次数可判断[L,R]是否为偶串。
注意到其实我们不关心具体个数,只关心每个字母出现次数的奇偶性,于是上述递推式中的加1可以改成异或1,从而
,表示在
中,数字j对应的字母出现次数的奇偶性。对于一个特定的
,就可以压缩成一个26位的二进制数g(i),特别地,g(0) = 0。判断[L, R]是否为偶串只需要检查g(R)异或g(L-1)是否为0。
可是上述算法的复杂度依然很高。对于任意的
,考虑所有以i为结束位置的子串中,有多少子串是偶串。注意到x异或x等于0。所以实际上就是在求
中值等于g(i)的个数。使用C++中的map可以快速的实现这一功能,复杂度
。
3代码
#include <bits/stdc++.h>
#define maxn 100009
using namespace std;
char s[maxn];
map<int,int>mp;
int n;
int main(){
scanf("%s",s);
n = strlen(s);
mp[0] = 1;
int cur = 0;
long long ans = 0;
for(int i = 0; i < n; i++){
int x = s[i] - 'a';
cur ^= (1 << x);
ans += mp[cur];
mp[cur]++;
}
cout << ans << endl;
return 0;
}