【问题标题】:Counting in Wonderland数数在仙境
【发布时间】:2015-10-15 09:30:33
【问题描述】:

text of Alice in Wonderland 包含 8 次“仙境”一词。 (让我们对这个问题不区分大小写)。

但是,如果您计算不连续的 子序列 以及子字符串,它会多次包含该词,例如。

要么井很深,要么她掉得很慢,因为她已经 有足够的时间,她下楼环顾四周,WONDER 接下来会发生。首先,她试图L记下弄清什么 她正要醒过来,但天太黑了,什么也看不见;

(子序列是一个序列,可以通过删除一些元素而不改变其余元素的顺序,从另一个序列派生出来。——维基百科)

这本书中有多少次包含 Wonderland 这个词作为子序列?我预计这将是一个很大的数字——这是一本很长的书,里面有很多 w 和 o 以及 n 和 d。

我尝试了蛮力计数(递归使循环深度为 10),但它太慢了,即使对于那个示例段落也是如此。

【问题讨论】:

  • w.*?o.*?n.*?d.*?e.*?r.*?l.*?a.*?n.*?d
  • 字符串wonderlandwonderland包含多少次子序列wonderland?两倍还是十倍?
  • @JerryFederspiel 是的,但是该正则表达式本质上等同于 OP 的 10 个 for 循环。这辈子都做不完。
  • @HighPerformanceMark 似乎是 17
  • @HighPerformanceMark,IVlad 是的 17。(我知道你是如何数 10 的,但在仙境中还有第二个 N 和 D 增加了更多。)

标签: algorithm math


【解决方案1】:

字符串“wonderland”作为爱丽丝梦游仙境中的子序列124100772180603281661684131458232次

主要思想是逐个字符扫描正文,保持目标字符串的每个前缀的运行频率(即:在这种情况下,“w”、“wo”、“won”、.. ., "wonderlan" 和 "wonderland") 一直到当前字母为止。这些运行计数很容易计算和更新。如果当前字母没有出现在“仙境”中,则计数保持不变。如果当前字母是“a”,那么我们将看到的“wonderla”的计数增加为到目前为止看到的“wonderl”的数量。如果当前字母是“n”,那么我们将“won”的计数增加“wo”的计数,将“wonderlan”的计数增加“wonderla”的计数。等等。当我们到达文本末尾时,我们将根据需要计算“wonderland”的所有前缀,包括字符串“wonderland”本身。

这种方法的优点是它需要单次遍历文本,并且不需要 O(n) 次递归调用(这可能会超过最大递归深度,除非你做一些聪明的事情)。

代码

import fileinput
import string

target = 'wonderland'

prefixes = dict()
count = dict()

for i in range(len(target)) :
    letter = target[i]
    prefix = target[:i+1]
    if letter not in prefixes :
        prefixes[letter] = [prefix]
    else :
        prefixes[letter].append(prefix)
    count[prefix] = 0L

for line in fileinput.input() :
    for letter in line.lower() :
        if letter in prefixes :
            for prefix in prefixes[letter] :
                if len(prefix) > 1 :
                    count[prefix] = count[prefix] + count[prefix[:len(prefix)-1]]
                else:
                    count[prefix] = count[prefix] + 1

print count[target]
  1. 使用古腾堡计划中的this text,从“第一章。落入兔子洞”开始,以“THE END”结束

【讨论】:

    【解决方案2】:

    假设您不想搜索wonderland,而只想搜索w。然后您只需计算故事中出现了多少次w

    现在假设你想要wo。对于您找到的当前模式的每个第一个字符,您将添加到计数中:

    1. 没有第一个字符的当前模式在故事的其余部分中出现了多少次,在你所在的这个字符之后:所以你已经将问题(story[1..n], pattern[1..n])减少到(story[2..n], pattern[2..n])

    2. 整个当前模式在故事的其余部分出现了多少次。所以你把问题减少到(story[2..n], pattern[1..n])

    现在您只需将两者相加即可。如果我们从子问题的角度来讨论,就不会多计。考虑示例wawo。显然,wo 出现了2 次。你可能会认为计数会是这样的:

    1. 对于第一个w,添加1,因为o在它之后出现一次,另一个1因为wo在它之后出现一次。

    2. 对于第二个w,添加1,因为o 在它之后出现一次。

    3. 答案是3,这是错误的。

    但这就是实际发生的情况:

    (wawo, wo) -> (awo, o) -> (wo, o) -> (o, o) -> (-, -) -> 1
                                                -> (-, o) -> 0
               -> (awo, wo) -> (wo, wo) -> (o, wo) -> (-, wo) -> 0
                                        -> (o, o) -> (-, -) -> 1
                                                  -> (-, o) -> 0
    

    所以你可以看到答案是2

    如果您没有找到w,那么这个位置的计数就是wo 在这个当前字符之后出现的次数。

    这允许使用记忆进行动态编程:

    count(story_index, pattern_index, dp):
      if dp[story_index, pattern_index] not computed:
        if pattern_index == len(pattern):
          return 1
        if story_index == len(story):
          return 0
    
        if story[story_index] == pattern[pattern_index]:
          dp[story_index, pattern_index] = count(story_index + 1, pattern_index + 1, dp) + 
                                           count(story_index + 1, pattern_index, dp) 
        else:
          dp[story_index, pattern_index] = count(story_index + 1, pattern_index, dp)
    
      return dp[story_index, pattern_index]
    

    拨打count(0, 0, dp)。请注意,您可以使代码更简洁(删除重复的函数调用)。

    Python 代码,没有记忆:

    def count(story, pattern):
      if len(pattern) == 0:
        return 1
      if len(story) == 0:
        return 0
    
      s = count(story[1:], pattern)
      if story[0] == pattern[0]:
        s += count(story[1:], pattern[1:])
    
      return s
    
    print(count('wonderlandwonderland', 'wonderland'))
    

    输出:

    17
    

    这是有道理的:对于故事的第一个 wonderland 中的每个 i 第一个字符,您可以将其与第二个 wonderland 中的剩余最终字符分组,为您提供 10 解决方案。另一个2 是单词本身。其他五个是:

    wonderlandwonderland
    *********    *
    ********    **
    ********    *      *
    **      **    ******
    ***      *    ****** 
    

    你说得对,这将是一个巨大的数字。我建议您要么使用大整数,要么将结果取模。

    相同的程序为您的示例段落返回9624

    【讨论】:

    • 我不明白数字2。你不是重复计算那些“wo”吗?每个这样的“wo”都将被计为数字 1 的一部分。对于后面的“w”,不是吗?
    • @EdwardDoolittle 我不太明白你的意思。数字 1 计数,对于每个 w,有多少 os 在它之后,形成一个 wo。它还计算有多少wo 在它之后(所以还要考虑下一个ws)。是的,1 中的w 将被算作woo 未来wos 被2 计算,但它们是不同的,非常有效,至少根据我对问题的理解。
    • 我将您的算法解读为“对于您找到的每个 'w'故事 }”。我明白你的意思是用不同的方式将这些陈述括起来......我想?
    • @EdwardDoolittle 我意识到我的解释有点混乱,因为它没有提到我们正在寻找的模式也会发生变化。它不是针对每个w,而是针对当前模式的每个第一个字符(我的实现没有这个错误)。添加了更多解释。如果这更清楚,请告诉我。
    【解决方案3】:

    跟进以前的 cmets,如果您正在寻找一种算法,该算法将返回 2 用于输入 wonderlandwonderland1 用于 wonderwonderland,那么我认为您可以从这个问题中调整算法:

    How to find smallest substring which contains all characters from a given string?

    实际上,您的情况的变化是,一旦找到该单词的一个实例,您就增加一个计数器并使用文本的其余部分重复所有过程。

    n 是文本的长度时,这种算法将是O(n),在m 是搜索字符串的长度的空间中是O(m)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-19
      • 1970-01-01
      • 2014-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多