前缀异或和问题是通过 前缀和 + 状态压缩 + 哈希表来处理连续 xxx 问题,发现是有规律的
通过前缀异或和获取每个位置的状态
使用哈希表记录每个状态第一次出现的位置
如果状态小于一定数量,可以通过状态压缩(BitMask)记录
问题
问题1 :统计美丽子数组数目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : long long beautifulSubarrays (vector <int >& nums) { long long res = 0 ; vector <int > temp (nums.size () + 1 ) ; for (int i = 0 ; i < nums.size (); i++) { temp[i + 1 ] = temp[i] ^ nums[i]; } unordered_map <int , int >dict; for (int x : temp) { res += dict[x]++; } return res; } };
这里有两个知识点:
子数组的异或和等于前缀和的异或和 AB ^ A = B
注意 res += dict[mod]++;
,可以理解为动态计算 1 + 2 + 3 + .... + n
问题2:和可被 K 整除的子数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int subarraysDivByK (vector <int >& nums, int k) { unordered_map <int , int > dict; dict[0 ] = 1 ; int res = 0 ; int s = 0 ; for (int i = 0 ; i < nums.size (); i++) { s += nums[i]; int mod = (s % k + k) % k; res += dict[mod]++; } return res; } };
这里的知识点表示:
C++取余是向0取整,所以就有
1 2 3 4 5 6 7 /4 = 1 7 /(-4 )= -1 7 %4 = 3 7 %(-4 )= 3 (-7 )/4 = -1 (-7 )%4 = -3
一个负数取余可能出现模为负数的情况,可以通过 mod = (s % k + k) % k
解决
问题3:找出最长的超赞子字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : int longestAwesome (string s) { int temp = 0 ; unordered_map <int , int > dict; int l = 0 ; dict[0 ] = -1 ; for (int i = 0 ; i < s.size (); i++) { temp ^= (1 << (s[i] - '0' )); if (dict.count(temp) != 0 ) { l = max (l, i - dict[temp]); } else { dict[temp] = i; } for (int k = 0 ; k < 10 ; k++) { if (dict.count(temp ^ (1 << k))) { l = max (l, i - dict[temp ^ (1 << k)]); } } } return l; } };
注意点:
为什么在查找 只差一位的情况时没有进行存储,因为当前前缀异或和的值是 temp
总结
遇到奇偶个数校验,想到 XOR
遇到有限的参数(小于20个)表状态, 想到状态压缩 (bitmask)
遇到求最长的连续子串使得和为k 想到 前缀和 + 哈希表 记录第一次出现某一状态的位置。
顺序练习
两数之和
和为K的子数组
和可被K整除的子数组
使数组和能被 P 整除
连续的子数组和
连续数组
面试题 17.05. 字母与数字
最美子字符串的数目
和相同的二元子数组
每个元音包含偶数次的最长子字符串
找出最长的超赞子字符串