给出一个整数N,它适合长(小于2 ^ 63-1)和50个其他整数。你的任务是找到1到N中有多少个数字不包含50个数字作为其子字符串?
这个问题来自一次采访。
答案 0 :(得分:22)
你没有指定基数,但我会假设小数而不失一般性。
首先,要认识到这是一个字符串问题,而不是数字问题。
构建有限自动机 A 以将50个整数识别为其他字符串的子串。例如,两个整数44和3被RE识别为子串。
^.*(44|3).*$
构建有限自动机 B 以识别小于 N 的所有数字。例如,以十进制识别1到27(含),可以通过编译RE
来实现^([1-9]|1[0-9]|2[0-7])$
计算自动机 A 和 B 的交叉点 C ,而这又是FA。
使用动态编程算法计算 C 识别的语言大小。从由 A 识别的语言大小中减去该值,由同一算法计算。
(我并不是说这是渐近最优的,但它足以解决许多Project Euler问题:)
答案 1 :(得分:22)
这只是larsmans已写的内容的解释。如果你喜欢这个答案,请另外投票给他。
有限自动机FA只是一组状态,规则说如果你处于状态S
并且你喂的下一个字符是c
然后你转换到州T
。其中两个州很特别。一个意思是“从这里开始”,另一个意思是“我成功匹配”。其中一个字符是特殊的,意思是“字符串刚刚结束”。所以你拿一个字符串和一个有限的自动机,从起始状态开始,继续将字符输入机器并改变状态。如果您提供任何状态意外输入,则无法匹配。如果您达到“我成功匹配”状态,您就会成功匹配。
现在有一种众所周知的算法,用于将正则表达式转换为匹配字符串的有限自动机,当且仅当该正则表达式匹配时。 (如果您已经阅读了正则表达式,这就是DFA引擎的工作方式。)为了说明我将使用模式^.*(44|3).*$
,这意味着“字符串的开头,任意数量的字符,后跟44或3 ,后跟任意数量的字符,后跟字符串的结尾。“
在我们寻找下一个字符时,首先让我们标记我们可以在正则表达式中的所有位置:^
A .*(4
B 4|3)
C {{1} }
我们的正则表达式引擎的状态将是这些位置的子集,并且特殊状态匹配。状态转换的结果将是我们在那个位置时可以获得的状态集,并且看到一个特定的字符。我们的起始位置是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
中启动,并遵循规则,您将匹配该模式。这个过程可能冗长乏味,但幸运的是可以实现自动化。我的猜测是larsmans已将其自动化以供自己使用。 (技术说明,从“RE中的位置”扩展到“可能存在的位置”可以在前面,在这里或在运行时完成。对于大多数RE,最好先做好。但就像这里一样。但是很少一些病态的例子会在很多状态下结束,而且在运行时这样做可能会更好。)
我们可以使用任何正则表达式执行此操作。例如,S1
可以标记为:^([1-9]|1[0-9]|2[0-7])$
A ^
B ([1-9]|1
C [0-9]|2
D [0-7])
我们获取状态:
$
和过渡:
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
好的,这是很多工作,但我们发现有3个字符串同时匹配这两个规则。我们这样做的方式是可自动化的,可扩展到更大的数字。
当然,我们最初提出的问题是有多少与第二个相匹配,而不是第一个。我们知道27匹配第二条规则,3匹配两者,所以24必须匹配第二条规则但不匹配第一条规则。
正如我之前所说,这只是larsmans解决方案的阐述。如果你学到了什么,就投票给他,投票给他答案。如果这些材料听起来很有意思,那就去买一本像 Progamming Language Pragmatics 这样的书,学习更多关于有限自动机,解析,编译等的知识。这是一个非常好的技能组合,并且有太多的程序员没有。