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]中一定出现偶数次。
  1. 对于当前前缀和,找到与之相等的前缀和个数cnt1;
  2. 对于当前前缀和,枚举每个二进制位并将该二进制位取反(假设该二进制位表示的字母出现奇数次),找到与某个二进制位取反后的前缀和相等的前缀和个数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 };

 

posted @ 2021-06-27 17:21  萌新的学习之路  阅读(178)  评论(0编辑  收藏  举报