leetcode threeSumMulti 的三种python解法(后两种特别好)

上周末参加了leetcode的考试, 做出两道题。不得不接受自己的愚笨。
第三题在看了排名前两名java的答案和另一个python做的答案之后,除了感叹人家算法的精妙,也只能暗下决心,要花更多的时间来刷题!
https://leetcode.com/contest/weekly-contest-106/problems/3sum-with-multiplicity/

这里整理一下上周末的第三道题。

第三道题是这样的。

Given an integer array A, and an integer target, return the number of tuples i, j, k  such that i < j < k and A[i] + A[j] + A[k] == target.

As the answer can be very large, return it modulo 10^9 + 7.

 

Example 1:

Input: A = [1,1,2,2,3,3,4,4,5,5], target = 8
Output: 20
Explanation: 
Enumerating by the values (A[i], A[j], A[k]):
(1, 2, 5) occurs 8 times;
(1, 3, 4) occurs 8 times;
(2, 2, 4) occurs 2 times;
(2, 3, 3) occurs 2 times.
Example 2:

Input: A = [1,1,2,2,2,2], target = 5
Output: 12
Explanation: 
A[i] = 1, A[j] = A[k] = 2 occurs 12 times:
We choose one 1 from [1,1] in 2 ways,
and two 2s from [2,2,2,2] in 6 ways.
 

Note:

3 <= A.length <= 3000
0 <= A[i] <= 100
0 <= target <= 300

简单的描述一下,就是给你一个数组,你找到数组里面所有的可能的按顺序的三个数a, b, c.使得他们加起来的值等于target.

这里先复盘一下我当时看到题时候是怎么想的。
我用的方法是用两个指针,分别从前后部分向中间走,target 减去两个指针就是第三个值。
但是我这里会出现一个问题。就是出现漏的情况。 我和小伙伴采取了一个稍微笨的方法。就是穷举所有可能,三遍for循环。然后拿到所有的可能之后进行去重。去重之后,再分别的计算每一个满足条件的组合。 这种情况我们最后做出来了,但是时间复杂度超了。。。

这里对之前思考的代码整理一下
from collections import Counter


class Solution(object):
    # def threeSumMulti(self, A, target):
    #     """
    #     :type A: List[int]
    #     :type target: int
    #     :rtype: int
    #     """

    def jiecheng(self, n):
        ret = 1
        while n:
            ret *=n
            n -=1
        return ret

    def cmn(self, m, n):
        return self.jiecheng(m)/self.jiecheng(n)/self.jiecheng(m-n)

    def threeSumMulti(self, l, target):
        leng = len(l)
        if leng <= 3:
            if sum(l) != target:
                return 0
            else:
                return 1
        if max(l) * 3 < target or min(l) * 3 > target:
            return 0
        c_l = Counter(l)
        if c_l.keys()[0] * 3 == target:
            m = c_l.keys()[0]
            rets = [[m,m,m]]
        else:
            l.sort()
            res_list = []
            for max_pos, max_value in enumerate(l):
                mid_pos = max_pos + 1
                min_pos = len(l) -1
                while mid_pos < min_pos:
                    v = l[max_pos] + l[mid_pos] + l[min_pos]
                    if v == target:
                        res_list.append([l[max_pos], l[mid_pos], l[min_pos]])
                        mid_pos +=1
                    elif v < target:
                        mid_pos +=1
                    else:
                        min_pos -=1
            ret_opreation = [sorted(i) for i in res_list]

            rets = []
            ret = [rets.append(i) for i in ret_opreation if not i in rets]
        print rets
        ret_sum = 0
        a = Counter(l)
        for i in rets:
            ones = 1
            i = Counter(i)
            i_key = i.keys()
            i_value = i.values()
            a_value = [a[j] for j in i_key]
            for h, r in zip(i_value, a_value):
                # print(h, r), "***"
                if r < h:
                    raise Error
                ones *= self.cmn(r, h)
            ret_sum += ones

        return ret_sum % (10**9 + 7)

leetcode做的最快的那个,用了一个计数排序的方法就高效的解决了这个问题。他的代码我们第一次复现为python的时候,用时大概92ms, 我自己看完后重写了一个用时大概200多秒。

之所以他会快,是因为人家好好读题了。题就在Note部分。 A的长度最大是3000. 但是A里面的每个值却不超过100. 这个时候用计数排序就会非常快。

大概的思路是先用一个数组/字典a对数组A里面出现的次数进行计数。然后a里面对应下标的位置里面存储的是当前数出现的次数。这样遍历两次100,第一次是第一个值,从0开始的,第二次就是第二个值,只要与第一次不重复即可。而第三个值就是target 减去第一个值再减去第二个值。

然后只要满足都小于等于100. 就可以分情况讨论了。代码如下:

class Solution:
    def threeSumMulti(self, A, target):
        """
        :type A: List[int]
        :type target: int
        :rtype: int
        """
        a = [0 for i in range(101)]
        for i in A:
            a[i] += 1
        ret = 0
        for i in range(101):
            for j in range(101):
                n = target - i - j
                if i <= j and n <= 100:
                    if i < j < n:
                        ret += a[i] * a[j] * a[n]
                    elif i == n and n < j:
                        ret += a[j] * a[i] * (a[i]-1) // 2
                    elif n == j and i < n:
                        ret += a[j] * a[i] * (a[j]-1) // 2
                    elif n == j == i:
                        ret += a[i] * (a[i]-1) * (a[i]-2) // 6
        return ret % (10 ** 9 + 7)

另外一种特别依赖python的实现方式。

from collections import defaultdict


class Solution:
    def threeSumMulti(self, A, target):
        """
        :type A: List[int]
        :type target: int
        :rtype: int
        """
        a1 = defaultdict(int)
        a2 = defaultdict(int)
        a3 = defaultdict(int)
        for i in A:
            for m, n in a2.items():
                a3[m + i] = (a3[m + i] + n) % (10**9 + 7)
            for m, n in a1.items():
                a2[m + i] = (a2[m + i] + n) % (10**9 + 7)
            a1[i] += 1
        return a3[target] % (10**9 + 7)

这种方式的巧妙之处在于它就像一个沙漏一样,一层一层的漏下来,最终流到最后一个沙漏的地方就是我们想要的结果集合。因为把所有的情况都给滤了一遍,所以它不会存在漏的情况。

猜你喜欢

转载自blog.csdn.net/funnyPython/article/details/83069381