【问题标题】:Understanding Count Triplets HackerRank了解 Count Triplets HackerRank
【发布时间】:2019-09-22 10:49:06
【问题描述】:

我一直在努力应对这个挑战:Count Triplets,经过大量的努力,我的算法并没有适用于每个测试用例。

自从在讨论中,我看到了一段代码并试图找出代码的真正功能,我仍然无法理解这段代码是如何工作的。

解决方案:

from collections import defaultdict

arr = [1,3,9,9,27,81]
r = 3
v2 = defaultdict(int)
v3 = defaultdict(int)
count = 0
for k in arr:
    count += v3[k]
    v3[k*r] += v2[k]
    v2[k*r] += 1
print(count)

上面的代码完美地适用于每个测试用例。我已经测试了kv2v3 的值以理解但仍然不明白代码如何在计数三元组时如此流畅地工作。我也无法在梦中想到那个解决方案。我想知道人们如何如此聪明地制定出这个解决方案。不过,如果我能得到正确的解释,我会很高兴。谢谢

k、v2、v3 的输出

from collections import defaultdict

arr = [1,3,9,9,27,81]
r = 3
v2 = defaultdict(int)
v3 = defaultdict(int)
count = 0
for k in arr:
    count += v3[k]
    v3[k*r] += v2[k]
    v2[k*r] += 1
    print(k, count, v2, v3)

输出

1 0 defaultdict(<class 'int'>, {1: 0, 3: 1}) defaultdict(<class 'int'>, {1: 0, 3: 0})                                                  
3 0 defaultdict(<class 'int'>, {1: 0, 3: 1, 9: 1}) defaultdict(<class 'int'>, {1: 0, 3: 0, 9: 1})                                      
9 1 defaultdict(<class 'int'>, {27: 1, 1: 0, 3: 1, 9: 1}) defaultdict(<class 'int'>, {27: 1, 1: 0, 3: 0, 9: 1})                        
9 2 defaultdict(<class 'int'>, {27: 2, 1: 0, 3: 1, 9: 1}) defaultdict(<class 'int'>, {27: 2, 1: 0, 3: 0, 9: 1})                        
27 4 defaultdict(<class 'int'>, {27: 2, 1: 0, 3: 1, 81: 1, 9: 1}) defaultdict(<class 'int'>, {27: 2, 1: 0, 3: 0, 81: 2, 9: 1})         
81 6 defaultdict(<class 'int'>, {1: 0, 3: 1, 243: 1, 81: 1, 9: 1, 27: 2}) defaultdict(<class 'int'>, {1: 0, 3: 0, 243: 1, 81: 2, 9: 1, 
27: 2})

【问题讨论】:

    标签: arrays python-3.x


    【解决方案1】:

    1。问题

    函数有两个参数,分别是:

    • arr:整数数组
    • r:一个整数,共同比率

    所以,输入可以是这样的

    arr: [1, 2, 2, 4]
    r: 2
    

    目标是返回形成几何级数的三元组的计数。


    2。如何解决它

    要解决它有多种方法。例如,从SagunB 基于comment from RobertsN

    • 可以在 O(n) 中完成 -> 单次通过数据
    • 不需要除法,只需要乘以 R 即可
    • 必须使用 map(C++) 或 dict(Java, Python) -> 可以是无序映射(节省 O(logN))
    • 在读取值时尝试向前思考 -> 该值以后会形成三元组的一部分吗?
    • 无需将 (R == 1) 视为极端情况
    from collections import Counter
    
    # Complete the countTriplets function below.
    def countTriplets(arr, r):
        r2 = Counter()
        r3 = Counter()
        count = 0
        
        for v in arr:
            if v in r3:
                count += r3[v]
            
            if v in r2:
                r3[v*r] += r2[v]
            
            r2[v*r] += 1
    
        return count
    

    或者像你说的

    from collections import defaultdict
    
    # Complete the countTriplets function below.
    def countTriplets(arr, r):
        v2 = defaultdict(int)
        v3 = defaultdict(int)
        count = 0
        for k in arr:
            count += v3[k]
            v3[k*r] += v2[k]
            v2[k*r] += 1
        return count
    

    3。最终结果

    两个案例都将通过HackerRank中当前的所有13个测试案例


    4。你的情况说明

    来自 RobertsN 的评论几乎解释了您的代码(与您的代码非常相似)。尽管如此,为了更好地了解代码的工作原理,只需打印计数发生的情况,v2 和 v3。

    假设你有输入

    4 2
    1 2 2 4
    

    预期的输出是

    2
    

    另外,我们知道 definition 的 v2 和 v3 看起来都像

    defaultdict(<class 'int'>, {})
    

    这让 for 循环留待理解。运算符 += 可能会引起一些混乱,但那是 already addressed by me in another answer

    所以,现在要了解其余部分,我们可以将循环更改为

    for k in arr:
        print(f"Looping...")
        print(f"k: {k}")
        print(f"v3_before_count: {v3}")
        count += v3[k]
        print(f"count: {count}")
        print(f"k*r: {k*r}")
        print(f"v3_before: {v3}")
        v3[k*r] += v2[k]
        print(f"v3[k*r]: {v3[k*r]}")
        print(f"v2[k]: {v2[k]}")
        print(f"v3_after: {v3}")
        print(f"v2_before: {v2}")
        v2[k*r] += 1
        print(f"v2_after: {v2}")
        print(f"v2[k*r]: {v2[k*r]}")
    

    会让你看到

    Looping...
    k: 1
    v3_before_count: defaultdict(<class 'int'>, {})
    count: 0
    k*r: 2
    v3_before: defaultdict(<class 'int'>, {1: 0})
    v2_before_v3: defaultdict(<class 'int'>, {1: 0})
    v3[k*r]: 0
    v2[k]: 0
    v3_after: defaultdict(<class 'int'>, {1: 0, 2: 0})
    v2_before: defaultdict(<class 'int'>, {1: 0})
    v2_after: defaultdict(<class 'int'>, {1: 0, 2: 1})
    v2[k*r]: 1
    Looping...
    k: 2
    v3_before_count: defaultdict(<class 'int'>, {1: 0, 2: 0})
    count: 0
    k*r: 4
    v3_before: defaultdict(<class 'int'>, {1: 0, 2: 0})
    v2_before_v3: defaultdict(<class 'int'>, {1: 0, 2: 0})
    v3[k*r]: 1
    v2[k]: 1
    v3_after: defaultdict(<class 'int'>, {1: 0, 2: 0, 4: 1})
    v2_before: defaultdict(<class 'int'>, {1: 0, 2: 1})
    v2_after: defaultdict(<class 'int'>, {1: 0, 2: 1, 4: 1})
    v2[k*r]: 1
    Looping...
    k: 2
    v3_before_count: defaultdict(<class 'int'>, {1: 0, 2: 0, 4: 1})
    count: 0
    k*r: 4
    v3_before: defaultdict(<class 'int'>, {1: 0, 2: 0, 4: 1})
    v2_before_v3: defaultdict(<class 'int'>, {1: 0, 2: 0, 4: 1})
    v3[k*r]: 2
    v2[k]: 1
    v3_after: defaultdict(<class 'int'>, {1: 0, 2: 0, 4: 2})
    v2_before: defaultdict(<class 'int'>, {1: 0, 2: 1, 4: 1})
    v2_after: defaultdict(<class 'int'>, {1: 0, 2: 1, 4: 2})
    v2[k*r]: 2
    Looping...
    k: 4
    v3_before_count: defaultdict(<class 'int'>, {1: 0, 2: 0, 4: 2})
    count: 2
    k*r: 8
    v3_before: defaultdict(<class 'int'>, {1: 0, 2: 0, 4: 2})
    v2_before_v3: defaultdict(<class 'int'>, {1: 0, 2: 0, 4: 2})
    v3[k*r]: 2
    v2[k]: 2
    v3_after: defaultdict(<class 'int'>, {1: 0, 2: 0, 4: 2, 8: 2})
    v2_before: defaultdict(<class 'int'>, {1: 0, 2: 1, 4: 2})
    v2_after: defaultdict(<class 'int'>, {1: 0, 2: 1, 4: 2, 8: 1})
    v2[k*r]: 1
    

    并提取所需的错误。我们可以从中观察到什么?

    • 最后一个循环中的计数从 0 增加到 2。
    • k 遍历 arr 的所有值 - 因此它将是 1、2、2 和 4。
    • 在初始循环中,v3_before_count 为 {},v3_before 为 {1:0}

    这个过程很可能会产生问题,回答这些问题会让你更深入地理解它。

    【讨论】:

      【解决方案2】:

      所以代码在遍历数组时会跟踪潜在的对和三元组。

      For each value in the array:
          // Increment count by the number of triplets that end with k
          count += v3[k]
          // Increment the number of potential triplets that will end with k*r
          v3[k*r] += v2[k]
          // Increment the number of potential pairs that end with k*r
          v2[k*r] += 1  
      

      任何给定 k 的三元组数是到目前为止我们遇到的任何给定 k/r 的对数。 请注意,在整个循环中,v3[k] 和 v2[k] 通常会为零,直到它们达到我们在上一次迭代中预测的 k*r 值。

      【讨论】:

        【解决方案3】:

        我一直在尝试理解它,最后,这段 C# 代码应该很容易理解

        static long countTriplets(List<long> arr, long r)
        {
            //number of times we encounter key*r
            var doubles = new Dictionary<long, long>();
            //number of times we encounter a triplet
            var triplets = new Dictionary<long, long>();
            long count = 0;
            foreach (var key in arr)
            {
                long keyXr = key * r;
        
                if (triplets.ContainsKey(key))
                    count += triplets[key];
        
                if (doubles.ContainsKey(key))
                {
                    if (triplets.ContainsKey(keyXr))
                        triplets[keyXr] += doubles[key];
                    else
                        triplets.Add(keyXr, doubles[key]);
                }
        
                if (doubles.ContainsKey(keyXr))
                    doubles[keyXr]++;
                else
                    doubles.Add(keyXr, 1);
            }
            return count;
        }
        

        【讨论】:

          【解决方案4】:
          from collections import defaultdict
          
          arr = [1,3,9,9,27,81]
          r = 3
          v2 = defaultdict(int)   #if miss get 0
          v3 = defaultdict(int)   #if miss get 0 
          count = 0`enter code here`
          for k in arr:
              #updating the count, starts propagating with delay=2 elements
              count += v3[k]
          
              # number of triplets with last component ending
              # on index i of k in array
              v3[k*r] += v2[k]
          
              # number of pairs with last component ending
              # on index i of k in array
              v2[k*r] += 1
          print(count)
          

          最好在示例中理解它 - 假设我们有数组 11111, 我们在 i=3 上,所以 111>1

          v2 当前计数为 111,11>11

          现在在 v3 中,我们从 v2 递归地构造计数,如下所示:对于使用 v2 创建和计数的每一对,我们分配最后一个组件,有 n 个这样的选项 对于#pairs = n。

          所以对于 i=3:

          11.1 (v2=1) //this pair remains by sum
          +
          .111 (v2=2) //these are new
          1.11 (v2=2)
          

          希望这会有所帮助!

          【讨论】:

            【解决方案5】:

            数字X的势值:如果有任何数字以X为优先级完全形成一个三联体,则存在三联体的数量。

            举个例子:1 2 4r = 2

            S1:使用1:没有三元组导致 1/2=0.5 和 1/2/2=0.25 不可用。将 1 添加到 hashmap。

            S2:2:如果达到最终数字(4),则可以形成 1 个潜在的三元组。将 2 添加到 hashmap。

            S3:4:如果达到最终数字(8),则可以形成 1 个潜在的三元组。将 4 添加到 hashmap。同时我们有 1 个三元组,因为 4/24/2/2 存在于 hashmap 中。

            但是我们怎么知道只有1?因为要到达三胞胎的第 4 号,你必须经过第 2 号,而我们在第 4 号之前只有 1 第 2 号。

            所以总数是 1。很容易。

            如果输入是1 2 2 2 4怎么办?

            我们有潜力:1:0; 2:1号1; 4: 3 个数字 2 => 3 个三元组

            1添加到输入,我们有:1 1 2 2 2 4r=2

            对于第一个 2,我们有 2 个潜在的三元组,因为在它之前有 2 数字 1。

            有了第二个2,我们有两倍

            第三个2,我们有三倍

            所以总数是 2(数字 1)x 3(数字 2)= 6 个潜在的三元组

            当索引达到数字 4 时,与上面的步骤 3 类似,我们的三元组总数为 6。

            这是我们遍历数组的 2 个 hashmap 的演示:

            Input Potential Count
            1 1 2 2 2 4 {1:0, 2:6, 4:3} {1:2, 2:3, 4:1}
            1 1 2 2 2 4 8 16 {1:0, 2:6, 4:3, 8:1, 16:1} {1:2, 2:3, 4:1, 8:1, 16:1 }

            作为上表中的第二个输入,我们可以说:

            三连音数字 (1,2,4) 我们有 6 个三连音(可能在数字 2)

            对于三元组 (2,4,8),我们有 3 个三元组(可能在 4 号)

            三连音数字 (4,8,16) 我们有 1 个三连音(可能在数字 8)

            所以总共是 10 个三胞胎。

            这是我的 javascript 解决方案

            function countTriplets(arr, r) {
            const numberAtThisPointOf = {};
            const potentialTripletOf = {};
            let total = 0;
            
            for (let i = 0; i < arr.length; i++) {
              const key = arr[i];
              if (numberAtThisPointOf[key] === undefined) {
                potentialTripletOf[key] = 0;
                numberAtThisPointOf[key] = 0;
              }
              
              // if key is final part of a triplet, mean the other 2 smaller numbers exist, the `key % r === 0` & `(key/r) % r === 0` to avoid decimal number in javascript
              if (key % r === 0 && numberAtThisPointOf[key/r] !== undefined & numberAtThisPointOf[key/r/r] !== undefined && (key/r) % r === 0) {
                total += potentialTripletOf[key/r];
              }
              
              // update potential case of current key
              if (numberAtThisPointOf[key/r] !== undefined && key % r === 0) {
                potentialTripletOf[key] += numberAtThisPointOf[key/r];
              }
              
              numberAtThisPointOf[key]++;
              
              }
              return total; 
            }
            

            【讨论】:

              猜你喜欢
              • 2020-11-24
              • 2018-09-13
              • 2019-12-18
              • 1970-01-01
              • 2018-06-17
              • 2012-04-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多