LeetCode 5799 最美字符串的数目
题目:https://leetcode-cn.com/problems/number-of-wonderful-substrings/
-
因为只关心字符出现的奇偶性 —> 可以用一个二进制位表示某个字母出现的奇偶性
-
根据数据范围,字符串中只包含前10个字母 —> 用10个二进制位表示字符串中出现的的每个字母的奇偶性
-
计算字符串中字母出现次数的前缀和 —> 只改变新增字符的奇偶性,可以用异或运算
前缀和pre[i]定义为:s[0…i]中各个字母出现的奇偶性; pre[i] = pre[i - 1] ^ (1 << (s[i] - ‘a’)); 前缀和之差 pre[j] - pre[i - 1]表示:子串s[i…j] 中每个字母出现次数的奇偶性;(本题中用异或运算表示) (1 << (s[i] - ‘a’)) 表示新增字符的二进制位为1,其他位为0; 该异或运算可以只改变单个二进制位的奇偶性。
-
最美字符串:至多一个字母出现奇数次 —> 1)所有字符都出现偶数次;2)枚举出现奇数次的字符的二进制位
如果下标不同位置的前缀和相等,则说明 前缀和i-1 以及 前缀和j 中各个字母出现的奇偶性是一样的。 即 pre[i - 1] = pre[j], 则子串 s[i…j] 中每种字母都出现偶数次。 (intuition: 偶数 - 偶数 = 偶数, 奇数 - 奇数 = 奇数) 所以如果前缀子串s[0…j]中某个字母出现的奇偶性和前缀子串s[0…i-1]中该字母出现的奇偶性一样,则该字母在子串s[i…j]中一定出现偶数次。
- 对于当前前缀和,找到与之相等的前缀和个数cnt1;
- 对于当前前缀和,枚举每个二进制位并将该二进制位取反(假设该二进制位表示的字母出现奇数次),找到与某个二进制位取反后的前缀和相等的前缀和个数cnt2;
1. pre[i] = pre[i - 1] ^ (1 << (s[i - 1] - ‘a’)); ans += cnt[pre[i]]; 2. for(int i = 1; i < 1024; i <<= 1) { ans += cnt[pre[i] ^ i]; }
代码:
1 class Solution { 2 public: 3 long long wonderfulSubstrings(string word) { 4 vector<long> cnt(1025, 0); 5 cnt[0] = 1; // 空字符串 6 int pre = 0; 7 long ans = 0; 8 for(char ch : word) { 9 pre ^= (1 << (ch - 'a')); 10 ans += cnt[pre]; 11 for(int i = 1; i < 1024; i <<= 1) { 12 ans += cnt[pre ^ i]; 13 } 14 cnt[pre]++; 15 } 16 return ans; 17 } 18 };