【问题标题】:Number of pairs that share at least one digit至少共享一位数字的对数
【发布时间】:2012-07-16 06:34:35
【问题描述】:

给你n 数字,你必须找到对数,这样它们之间至少有一个数字是共同的。

例如。对于 5 个数字:

2837 2818 654 35 931

答案:6

这里的对是(2837,2818), (2837,35), (2837,931), (2818,931), (654,35), (35,931)

我的尝试:我采用了一个结构,该结构存储十进制数字、数组中数字形式的数字以及该数字中的位数。。 p>

现在对于每个数字,我散列数组中包含索引 0-9 的数字,并检查所有以下数字是否已经存在任何数字。 p>

我的尝试是O(n^2),速度很慢。有没有其他算法会更快?

【问题讨论】:

  • 您对一个值进行了哈希处理,但未能使用哈希表查找?
  • 我的解决方案工作正常,但是当 n 很大时花费了很多时间
  • 这不能比 O(n^2) 更快,因为您至少需要 O(n^2) 才能在最坏的情况下生成答案。
  • @GrigorGevorgyan - 我假设问题中示例的答案只是6。您不必列出所有对。
  • @JirkaHanika 是的,你是正确的配对被显示为更多和更清晰的解释,只需要计数作为答案,即使有问题,它也粗体写为“配对数”。

标签: algorithm


【解决方案1】:

在这里了解什么是变量和什么是常数是至关重要的。

位数是一个常数 (10)。所有数字集的数量(1024)也是如此。所有此类集合对的数量也是如此(220,或大约一百万)。让我们利用这一点。

让我们尝试将输入预处理为大小恒定(与输入大小无关)的数据结构中的“等价”表示。然后,我们对这个恒定大小结构所做的任何事情,根据定义都是恒定时间操作,因此总运行时间仅由预处理阶段渐近确定。

数据结构

创建一个1024个整数的数组,每个桶(索引)对应一组数字;我们希望在每个桶中存储恰好具有该组数字的输入数字的数量。

例如 3606 有数字 0、3 和 6,所以它会被计入桶 20 + 23 + 26 = 73。

算法

预处理是显而易见的。取下一个数字(例如'3'),将其转换为它的值(例如3),现在计算相应的位(例如1 << 3)并将其OR转换为(暂定)桶索引变量;不同的数字占据不同的位,因此每个数字组合都有一个唯一的桶,但我们摆脱了任何重复的数字。像这样循环,直到遇到数字分隔符;此时,桶索引是最终的,我们可以增加桶,重置桶索引并跳到下一个数字。

就是这样。剩下的就是数我们的羊了。哎呀。成对的羊。

将每个存储桶与其他存储桶(但不是自身)进行比较。每当两个索引共享一个数字(这可以使用& 运算符确定)时,将这两个存储桶的内容相乘,然后将乘积添加到全局维护的总和中。

将每个存储桶也与其自身进行比较,并仅将x * (x - 1) / 2 添加到全局维护的总和中,其中x 是存储桶的内容。

这个总和就是你的结果。

性能

最坏情况:O(n) 其中n 是输入大小。

常数因子也是有利的。每个数字或分隔符我们需要一些指令(和 RAM 访问);恒定阶段检查一百万个桶对,在每对桶上花费一些类似的指令(不需要访问 RAM,数据结构非常紧凑)。这是闪电般的速度。

理论家会说这是作弊。我们假设输入长度没有上限(或者我们根本不能谈论渐近复杂度),但我们还假设我们可以将总输入长度放入一个整数变量中。哦,好吧。

更实用的程序员会注意到,该算法的字母大小呈指数级增长。我们很幸运;如果我们的单词不是由数字组成,而是由除分隔符之外的任意字符组成,那么我们的单词仍将是渐近线性时间算法,但与可以轻松处理到兆字节的幼稚算法相比,它对于任何输入都会非常慢一次输入。

【讨论】:

  • 我认为这种形式的问题与我提出的解决方案相同,但这个问题可能是可以解决的。
  • 对不起,我错了。我以为你会重复计算具有多个共同数字的对,但你没有。
  • 能否请您解释一下为什么使用 2^0 + 2^3 + 2^6 这种方法进行映射。我更想知道您是如何想到的。
  • 任何固定映射都可以,只要它从不将两组不同的数字映射到同一个代码。我选择这个是为了空间效率、缓存友好性以及最后的嵌套迭代的方便性:使用这种特定的映射,数组中没有“洞”(有效代码之间没有非代码)。最后但并非最不重要的一点是,这种编码在任何正常架构上每个输入字符只需要少量机器指令。不需要求幂运算,因为对于任何 x,2^x = 1 左移 x。
  • @JirkaHanika,你能详细解释一下吗,我没有清楚地理解上面的解决方案,你能请..请
【解决方案2】:

首先,我注意到常用数字的位置无关紧要。

在这种情况下,我用哈希表勾勒出一个小算法:形成 10 个 bin,每个数字一个。然后,对于每个数字,将(唯一地)该数字的 ID 放入每个 bin 中,对应于它所具有的每个数字。这个操作是O(n*k),k是数字的位数。最后,要形成所有对,在每个 bin 中取成对的数字。要删除可能的双倍数,请将每对 (a,b) 与 a

我认为最坏的情况实际上是 O(n^2);事实上,我确实认为这一步必须具有 O(n^2) 的复杂性,因为您想要获取所有对(最大 n*(n+1)/2)。所以最终的复杂度确实是二次的。

【讨论】:

    【解决方案3】:

    创建一个集合数组,每个数字一个。

    迭代你的数字并将每个数字放入每个集合中的数字。

    迭代所有 10 个集合并将集合中的每个元素与同一集合中的所有其他元素组合。 (或所有其他大于自身的元素,如果您不想在结果中出现 (a,b) 和 (b,a)。

    我认为这仍然是 O(n^2),但可以使用 fork join 方法很好地使其瘫痪。

    更新

    刚刚意识到您只想要结果的数量。所以这将是所有集合的 size * size-1 的总和。由于插入一个集合并获得它的大小应该是线性的(我认为)这实际上可能是 O(n)

    另一个更新

    如果您的号码不同,并且您只对配对数量感兴趣,那么您甚至不需要集合,您只需要一个计数器。

    不起作用 来自 cmets:

    Consider 1st pair in above questions test case (2837,2818), this will put first number in set containing digit 2 and 8 and same for 2818 now they are to be counted as one but counting in 2 and 8 will count it twice. I hope you understand what I am trying to say...
    

    所以这种方法行不通……我想它可能对其他人有警告价值。

    【讨论】:

    • 枚举对,而不是仅仅计算它们的数量,在最坏的情况下必然需要 Ω(n²) 时间,因为它们有 O(n²) 个。
    • @larsmans 你是对的,我刚刚意识到只需要对数。编辑了答案。
    • @L.ppt 数字可能很大是什么意思?你的意思是n可以很大吗?或者你的意思是每个数字都可以很大?如果 n 可以达到 10^18,我认为你有问题。
    • @L.ppt: 哪个十进制数字(不是数字!)大于 9?
    • 所以数字最多有 18 位数字......因为这只是 O() 估计中的一个常数,而不是一个很大的常数,所以它几乎无关紧要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-26
    • 1970-01-01
    • 2021-12-22
    • 2022-01-22
    • 1970-01-01
    • 2019-06-21
    相关资源
    最近更新 更多