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;
    }
};
View Code

 

posted on 2018-12-14 00:49  _isolated  阅读(342)  评论(0)    收藏  举报