前言
位运算是计算机最基本的计算,是最快的运算方式,与或非各有特点;抽象能力考察我理解成一种 拿核心去累赘 的能力;分组快速统计,我们不必把眼光放在一个个数上,也许没有实际意义,而是针对所需,进行抽象分组。
一、优质数对的数目
二、思路与优化过程
package competition.single303;
import java.util.HashSet;
import java.util.Set;
public class CountExcellentPairs {
/*
前后数有什么关系?数字往后增长,
与将都有1的地方算上,或都有1,0/1全算上。
当两数或时,非重叠部分的1算上了,重叠部分的1只算了一半,但是与刚好不算非重叠的部分,且重叠的部分算了一半。
那么两个数1的个数和,就是其与之后 + 或之后的1的个数。
*/
public long countExcellentPairs(int[] nums, int k) {
// 分组快速统计思想。
// 我们不关心是那个数和那个数组成的优质对,我们只关心那个数有多少bit = 1,关心两者的bit = 1的个数之和是否大于k而已。
// 可以去掉一些与题无关的累赘记录 & context,直面问题,可以降低时间复杂度,甚至是解题的关键。
// idea:将bit = 1相同的数记录其有多少个,只要bit数等于某某及其它的个数,这才是核心信息,抓住命脉。
int[] bit = new int[33];// 分组思想,将bit数抽象出来,而不关心其他累赘信息。
Set<Integer> pool = new HashSet<>();// 去重。
for (int num : nums) {
if (!pool.contains(num)) bit[Integer.bitCount(num)]++;
pool.add(num);
}
/*
// 为什么我会写出这样的代码,找到思路了又能怎样,不清楚自己想要什么,不清楚与问题的联系点,照样写不出低复杂度的代码。
int[] bit = new int[nums.length];
for (int i = 0; i < nums.length; i++) bit[i] = Integer.bitCount(nums[i]);
*/
long ans = 0;
for (int i = 0; i < 33; i++) {
for (int j = Math.max(0, k - i); j < 33; j++) {
ans += bit[i] * bit[j];
}
}
return ans;
/*
review
多分析分析,找找已经操作/条件和问题的联系,在纸上多写写,找找规律,找找感觉。
*/
}
}
总结
1)运行算各具特点,位运算相关的题,也在纸上多写写画画,找找感觉和规律。
2)平时多练,竞赛的时候没那么好的精力条件去分析太多和太深的东西,通过练习把自己的思考下限/脑活跃度下限提上来。
3)抽象能力可简单理解为拿核心去杂质。
4)像这种分组快速统计套路是第二次见了,大幅度降低时间复杂度,核心也是抓命脉,去无用信息,让有用信息不因与本题无关的无用信息牵绊其无法压缩。
参考文献
[1] LeetCode 优质数对的数目