LeetCode.923 三数之和的多种可能
一篇对我很有启发的博客:k数和问题 https://blog.csdn.net/qq_17550379/article/details/83023017
对于任意的k数和问题,复杂度都可以简化到:O(n^(k/2) * logn)
具体做法,就是:
【偶数情况:
1:首先枚举 k/2 个数的和,并把所得的所有结果,加到一个“查找表”中,每一个“和”仅占一个存储空间。
(本来还一直想着用map<int,int> 键值对,后来发现根本不用,int cnt[max] 即可。)
(所有的 map<int,int> 本质上都可以用 cnt[max] 替代,唯有 map<string,int> 才有用map的必要)
2:对上面所得的查找表,再次枚举,每枚举一个数 x ,就以 O(log) 在表中查找 target-x
(map、set或者二分查找的复杂度都是log,一般用二分查找)
【奇数情况:(嘤嘤嘤我还没有掌握)
1:枚举 k-k^(1/2) 的和
2:对查找表内每个数x,查找是否有 a 和 target - a - x 的数对 ;
具体到这一题:先枚举两两的和 ,假设和为sumx,然后看 target-sumx 在不在原始数据集里即可。
对于这一题,这里要注意的是:
“枚举” 的,不是 “每个数的下标值”,而是“数本身的值”(一个数值可能对应多个数)
因为这道题中,注意数据范围:
数字的个数很多( 0< n < 3000 ),但是数字数值的分布范围却很小( 0<= A[i] <= 100 )
这一点也是在暗示:不要枚举“每个数字”,要枚举“数值”。
有一个问题:枚举“数值”,可能会造成“同一个数字被重复选取” 的问题
(因为只记录了数值信息,没有记录“id”之类的信息)。
所以最终的做法就是,先正常枚举前两个数的数值,
然后每一次枚举,都把“此次要验证的三个数”截取下来,对它们的数值,进行讨论:
1): a == b == c
2): a == b != c
3): a != b != c
(计数永远有个大问题 ———— 如何解决重复枚举的问题?)
(附上大大的题解链接:https://blog.csdn.net/qq_17550379/article/details/83088746 )
我的小垃圾代码:
class Solution { public: int threeSumMulti(vector<int>& A, int target) { int mod = 1e9 + 7; int cnt[4000]; memset(cnt,0,sizeof(cnt)); for(int i=0;i<A.size();i++) cnt[A[i]]++; long long sum = 0; for(int i=0;i<=100;i++) { for(int j=0;j<=100;j++) { int tmp_c = target - i - j; if(tmp_c<0) continue; // 注意这里要开longlong,不然在下方, += 右边,会爆int long long num_i = cnt[i]; long long num_j = cnt[j]; long long num_c = cnt[tmp_c]; // 注意这里,必加 num_i>=2。( // 因为我的cnt是数组,不是hashmap,枚举到的不一定是“真的存在”的。 // 如果num_i<2 ,sum 会加上负数,这就凉了; // 所以要先拦截掉) if(i==j && j==tmp_c && num_i>=2) { sum += (num_i%mod*(num_i-1)%mod*(num_i-2)/6)%mod; sum %= mod; } else if(i<j && j<tmp_c){ //注意,是小于号,解决重复枚举的问题 sum += (num_i%mod*num_j%mod*num_c)%mod; sum %= mod; } // 只算一个 i!=j==tmp_c 即可,无需再算 i==tmp_c 和 j==tmp_c 的情况 //(那些属于重复枚举) else if(i!=j && j==tmp_c && num_j>=1){ sum += (num_i %mod* num_j %mod*(num_j-1)/2)%mod; sum %= mod; } sum = sum%mod; } } return sum; } };
浙公网安备 33010602011771号