【问题标题】:Algorithm for exclusion of numbers排除数字的算法
【发布时间】:2012-07-03 15:20:04
【问题描述】:

给你一个整数 N,它适合 long(小于 2^63-1)和 50 个其他整数。你的任务是找出从 1 到 N 有多少个数字不包含这 50 个数字的子串?

这个问题来自采访。

【问题讨论】:

  • @AakashM 这里有一个编程问题。
  • @btilly 这让我松了一口气。也许可以编辑这个问题,让像我这样在这里只看到数学问题的人明白这一点?
  • @AakashM 显然问题是要编写一个程序来进行计算。正如 larsmans 的回答所表明的那样,其中涉及到很多编程理论。
  • 数学是计算机科学的基础。算法处于两者的边界。没有理由将它们排除在外。
  • 恭喜各位版主。无论是否有足够多的人在您不注意后取消删除并重新打开此问题,您都已说服另一位贡献者离开。再见。

标签: algorithm language-agnostic


【解决方案1】:

您没有指定基数,但我将假定为十进制而不失一般性。

首先,认识到这是一个字符串问题,而不是数字问题。

构建一个有限自动机A,将这 50 个整数识别为其他字符串的子字符串。例如,两个整数 44 和 3 被 RE 识别为子字符串

^.*(44|3).*$

建立一个有限自动机B来识别所有小于N的数。例如,要识别十进制的 1 到 27(包括),这可以通过编译 RE 来实现

^([1-9]|1[0-9]|2[0-7])$

计算自动机AB的交集C,这又是一个FA。

使用动态编程算法来计算 C 识别的语言的大小。从由 A 识别的语言的大小中减去它,由相同的算法计算得出。

(我并不是说这是渐近最优的,但它的速度足以解决许多欧拉计划问题:)

【讨论】:

  • 应该是 1 到 27(含)而不是 0 到 27。但是非常好的方法。它肯定胜过我所想的包含-排除。
  • 我很抱歉,但我不明白解决方案.. cud 你用一个例子解释.. 也许一个更大的.. 你能告诉我你的动态编程解决方案的状态
  • @larsmans 请查看我的回复,该回复只是试图阐明您的解释并给我反馈、更正等。
  • 我认为您也可以使用二元决策图来做到这一点 - 事实上,如果得知它们与有限状态机之间存在联系,我不会感到惊讶。 BDD 的一个优点是对它们的许多操作都有多项式保证。
【解决方案2】:

这只是对 larsmans 已经写过的内容的解释。如果你喜欢这个答案,请另外给他投票。

有限自动机 FA 只是一组状态,并且规则说,如果您处于状态 S 并且输入的下一个字符是 c,那么您将转换到状态T。其中两个州是特殊的。一种表示“从这里开始”,另一种表示“我成功匹配”。其中一个字符很特殊,意思是“刚刚结束的字符串”。所以你拿一个字符串和一个有限自动机,从起始状态开始,不断地将字符输入机器并改变状态。如果您提供任何状态的意外输入,您将无法匹配。如果你达到“我成功匹配”的状态,你就成功匹配了。

现在有一种众所周知的算法可以将正则表达式转换为一个有限自动机,当且仅当该正则表达式匹配时,它才匹配字符串。 (如果您阅读过正则表达式,这就是 DFA 引擎的工作原理。)为了说明,我将使用模式 ^.*(44|3).*$,这意味着“字符串的开头,任意数量的字符,后跟 44 或 3,然后任意数量的字符,后跟字符串的结尾。"

首先让我们在寻找下一个字符时标记正则表达式中的所有位置:^A.*(4B4|3)C.*$

我们的正则表达式引擎的状态将是这些位置的子集,并且匹配的特殊状态。状态转换的结果将是我们在那个位置看到特定角色时可以达到的状态集。我们的起始位置在 RE 的开头,即 {A}。以下是可以达到的状态:

S1: {A}   # start
S2: {A, B}
S3: {A, C}
S4: {A, B, C}
S5: matched

以下是过渡规则:

S1:
  3: S3
  4: S2
  end of string: FAIL
  any other char: S1
S2:
  3: S3
  4: S3
  end of string: FAIL
  any other char: S1
S3:
  4: S4
  end of string: S5 (match)
  any other char: S3
S4:
  end of string: S5 (match)
  any other char: S4

现在,如果您使用任何字符串,请从状态 S1 开始,并遵循规则,您将匹配该模式。这个过程可能漫长而乏味,但幸运的是可以自动化。我的猜测是 larsmans 已将其自动化以供自己使用。 (技术说明,从“RE 中的位置”到“您可能处于的位置集”的扩展可以像这里一样预先完成,也可以在运行时完成。对于大多数 RE,最好预先完成, 就像这里一样。但是一小部分病态的例子会以非常多的状态结束,最好在运行时做这些。)

我们可以使用任何正则表达式来做到这一点。例如^([1-9]|1[0-9]|2[0-7])$ 可以被标记为:^A([1-9]|1B[0-9]|2C[0-7])D$ 我们得到状态:

T1: {A}
T2: {D}
T3: {B, D}
T4: {C, D}

和过渡:

T1:
  1: T3
  2: T4
  3-9: T2
  any other char: FAIL
T2:
  end of string: MATCH
  any other char: FAIL
T3:
  0-9: T2
  end of string: MATCH
  any other char: FAIL
T4:
  0-7: T2
  end of string: MATCH
  any other char: FAIL

好的,所以我们知道什么是正则表达式,什么是有限自动机,以及它们之间的关系。两个有限自动机的交集是什么?它只是一个有限自动机,当两个有限自动机单独匹配时匹配,否则不匹配。它很容易构造,它的状态集只是一个状态和另一个状态的对的集合。它的转换规则是只对每个成员独立应用转换规则,如果其中一个失败,则整体执行,如果两者都匹配,则两者都执行。

对于上述对,让我们实际执行数字13 上的交集。我们从状态(S1, T1)开始

state: (S1, T1)  next char: 1
state: (S1, T3)  next char: 3
state: (S3, T2)  next char: end of string
state: (matched, matched) -> matched

然后在号码上14

state: (S1, T1)  next char: 1
state: (S1, T3)  next char: 4
state: (S2, T2)  next char: end of string
state: (FAIL, matched) -> FAIL

现在我们来到了这个重点。给定最终的有限自动机,我们可以使用动态规划来计算有多少字符串匹配它。这是计算:

0 chars:
  (S1, T1): 1
    -> (S1, T3): 1 # 1
    -> (S1, T4): 1 # 2
    -> (S3, T2): 1 # 3
    -> (S2, T2): 1 # 4
    -> (S1, T2): 5 # 5-9
1 chars:
  (S1: T2): 5      # dead end
  (S1, T3): 1
    -> (S1, T2): 8 # 0-2, 5-9
    -> (S2, T2): 1 # 3
    -> (S3, T2): 1 # 4
  (S1, T4): 1
    -> (S1, T2): 6 # 0-2, 5-7
    -> (S2, T2): 1 # 3
    -> (S3, T2): 1 # 4
  (S2, T2): 1      # dead end
  (S3, T2): 1
    -> match:    1 # end of string
2 chars:
  (S1, T2): 14     # dead end
  (S2, T2): 2      # dead end
  (S3, T2): 2
    -> match     2 # end of string
  match:    1
    -> match     1 # carry through the count
3 chars:
  match:    3

好的,工作量很大,但我们发现有 3 个字符串同时匹配这两个规则。我们以一种可自动化且可扩展到更大数量的方式来做到这一点。

当然,我们最初提出的问题是有多少匹配第二个而不是第一个。好吧,我们知道 27 匹配第二条规则,3 匹配两者,所以 24 必须匹配第二条规则而不是第一条。

正如我之前所说,这只是 larsmans 解决方案的阐述。如果您学到了一些东西,请为他投票,为他的答案投票。如果这些材料听起来很有趣,那就去买一本Progamming Language Pragmatics 之类的书,了解更多关于有限自动机、解析、编译等的知识。这是一个非常好的技能组合,而太多的程序员没有。

【讨论】:

    猜你喜欢
    • 2022-11-28
    • 1970-01-01
    • 2010-12-03
    • 1970-01-01
    • 1970-01-01
    • 2020-07-06
    • 2012-02-29
    • 2016-09-13
    • 1970-01-01
    相关资源
    最近更新 更多