如何从字符串中对所有可能的单词进行排序?

时间:2009-10-08 15:36:26

标签: python

我想知道如何继续执行此任务,请将此字符串用作“thingsandstuff”。

我怎样才能从这个字符串中生成所有可能的字符串,以便根据英语字典单独查找它们?

目标是在不包含空格的字符串中查找有效的英语单词。

由于

11 个答案:

答案 0 :(得分:5)

另一种可能性是反过来,而不是从字符串生成子字符串,抓取所有候选单词并将它们与字符串匹配。

您可以存储原始字符串中单词的索引对(开始,结束)。

这可以很容易地在正则表达式中完成,或者,如果性能不够,可以使用str.find(),或者甚至不能使用更复杂的字典索引方案或关于什么可以匹配和不匹配的智能(参见{ {3}}提出意见)

这里有一个我的意思的样本

candidate = "thingsandstuffmydarlingpretty"
words = file('/usr/share/dict/words').read()
#This generator calls find twice, it should be rewritten as a normal loop
generate_matches = ((candidate.find(word),word) for word in words.split('\n')
                     if candidate.find(word) != -1 and word != '')

for match in generate_matches:
    print "Found %s at (%d,%d)" % (match[1],match[0],match[0] + len(match[1]))

答案 1 :(得分:5)

人们谈论这个,好像问题的顺序是可能的子串的数量。这是不正确的。这个问题的正确顺序是:

O(min(字母数字,子字符串数组合)* comparison_cost)

因此,在Vinko上构建问题的另一种方法是将词典中的内容编入索引(例如,对于词典中的每个作品,确定该词中的字母,这个词的长度等)。这可以大大加快速度。作为一个例子,我们知道目标“女王”不能匹配“斑马”(没有z!)(或任何包含z,r,b,a ......的单词)等。此外,将dict中的每个单词存储为排序字符串('zebra' - >'aberz')并执行“string in string”(最长公共子字符串)匹配。 'eenuq'vs'abarz'(不匹配)。

(注意:我假设原始单词中的字母顺序无关紧要 - 这是一个'字母包',如果有,则相应调整)

如果您有很多单词可以同时进行比较,则可以使用KMP之类的内容进一步降低比较成本。

(另外,我直接进去,做了一些亚历克斯没有的假设,所以如果他们错了,那就闭口!)

答案 2 :(得分:3)

蛮力方法,即检查每个子字符串,即使对于中等长度的字符串(长度为N的字符串具有O(N**2)子字符串),在计算上也是不可行的。除非你关心的字符串长度有一个非常严格的限制,否则这种情况不会很好。

为了使事情变得更可行,需要更多的知识 - 你是否对重叠词感兴趣(例如你的例子中的'事物'和'沙子')和/或会留下下落不明的词字符(例如你的例子中的'thing'和'and',中间's'搁浅),或者你想要将字符串严格划分为并列(不重叠)字而没有残留?

后者将是最简单的问题,因为自由度急剧下降 - 主要是试图确定两个相邻字符之间的“断点”序列,将字符串拆分为单词。如果是这种情况,你是否需要每一个可能的有效分割(即你需要两个“东西沙”“东西和”),或者任何一个有效的分裂都需要,或者您的分割必须优化的标准是什么?

如果您澄清所有这些问题,可能会给您更多帮助!

答案 3 :(得分:2)

norving写了一篇关于如何在python中编写拼写检查器的精彩文章。

http://norvig.com/spell-correct.html

它将为您提供如何检测单词的好主意。 (即只是测试每组字符,直到你得到一个有效的字......要注意确定性的,你需要反过来。测试所有的字符串,然后去除最后的字符。这样你得到复合这些词是因为它们是......或者没有意图,谁知道。空间有原因:)

之后,它是基本的CS 101。

答案 4 :(得分:1)

这将找出候选人是否可以由给定单词中的字母组成;我们假设word(但不是candidate)在通话之前排序。

>>> def match(candidate, word):

        def next_char(w):
            for ch in sorted(w):
                yield ch

        g = next_char(word)
        for cl in sorted(candidate):
            try:
                wl = g.next()
            except StopIteration:
                return False
            if wl > cl:
                return False
            while wl < cl:
                try:
                    wl = g.next()
                except StopIteration:
                    return False
                if wl > cl:
                    return False
        return True

>>> word = sorted("supernatural")
>>> dictionary = ["super", "natural", "perturb", "rant", "arrant"]
>>> for candidate in dictionary:
     print candidate, match(candidate, word)

super True
natural True
perturb False
rant True
arrant True

当我加载BSD单词文件(235,000+个单词)并使用plenipotentiary作为我的单词运行时,我在一秒半内得到大约2500个单击。

如果你要进行多次搜索,最好从next_char中删除排序,建立一个字典,键入每个单词的排序版本 -

d = dict([(sorted(word), word) for word in dictionary])

并通过以下逻辑生成结果:

result = [d[k] for k in d.keys() if match(k, word)]

所以你必须一遍又一遍地执行250,000多种。

答案 5 :(得分:0)

这是我的想法

  • 查找原始
  • 中包含1个字符的所有可能字符串
  • 查找原始
  • 中包含2个字符的所有可能字符串
  • ......与原始字符串的长度相同

然后添加所有内容并与您的词典匹配

答案 6 :(得分:0)

如果您将其分解为音节,然后使用这些音节构建单词以与您的字典进行比较,该怎么办?它仍然是一种蛮力方法,但它肯定会加快速度。

答案 7 :(得分:0)

我查看了一个powerset实现。太多的可能性。

尝试编码字符串和字典中的所有候选人,并查看字典中的候选人是否可以来自候选字符串。也就是说,字典单词中的字母是否比候选字符串中的字母更频繁出现?

from __future__ import with_statement
import collections

def word_dict(word):
    d = collections.defaultdict(int)
    for c in word:
        d[c] += 1
    return d

def compare_word_dict(dict_cand, cand):
    return all(dict_cand[k] <= cand[k] for k in dict_cand)


def try_word(candidate):
    s = word_dict(candidate)
    dictionary_file = r"h:\words\WORDs(3).txt"
    i = 0
    with open(dictionary_file) as f:
        for line in f:
            line = line.strip()
            dc = word_dict(line)
            if compare_word_dict(dc,s):
                print line
                i += 1
    return i


print try_word("thingsandstuff")

我的字典里有670个单词。似乎有点小。字典中的200k字词大约需要3秒钟。

适用于python 2.5 and above because of the addition of collections.defaultdict。在python 3.1中,添加了collections.Counter,其作用类似于collections.defaultdict(int)。

答案 8 :(得分:0)

看看this post,它在Python和OCaml中解决了同样的问题,其解决方案是首先对字符串进行规范化,而不是进行暴力搜索。

顺便说一下,自动翻译会删除缩进,所以要获得正常工作的Python代码,你应该看一下untranslated Spanish version(实际上它比谷歌翻译生成的蹩脚英语要好得多)......

编辑:

重新阅读你的问题,我现在明白你只想要那些被解读的词,对吧?如果是这样,您不需要完成帖子中描述的所有内容,只需:

maxwordlength = max(map(len, english_words))
for i in range(len(word)):
    for j in range(i+1, min(maxwordlength+i, len(word))):
         if word[i:j] in english_words:
             print word[i:j]

现在复杂性应该是O(N),因为英语中最大单词的大小是有限的。

答案 9 :(得分:0)

代码:

def all_substrings(val):
    return [val[start:end] for start in range(len(val)) for end in range(start + 1, len(val))]

val = "thingsandstuff"
for result in all_substrings(val):
    print result

输出:

t
th
thi
thin
thing

[...]

tu
tuf
u
uf
f

答案 10 :(得分:0)

如果您事先知道完整字典,并且搜索之间没有变化,您可以尝试以下内容...

索引字典。每个单词(例如“hello”)变成(键,数据)元组,例如(“ehllo”,“hello”)。在键中,字母按字母顺序排序。

良好的索引数据结构将包括trie(也称为数字树)或ternary tree。可以使传统的二叉树起作用。哈希表不起作用。我将假设一个特里树或三元树。注意 - 数据结构必须充当多图(您可能需要在每个密钥匹配的叶子上匹配数据项的链接列表)。

在评估特定字符串之前,请对字符串中的字母进行排序。然后在数据结构中进行密钥搜索。 BUT 简单的密钥搜索只会找到使用原始字符串中所有字母的字词。

基本上,trie搜索一次匹配一个字母,根据输入的下一个字母选择子节点。但是,在每一步中,我们都有一个额外的选项 - 跳过已排序的输入字符串的字母并保留在同一节点(即,不要在输出中使用该字母)。显而易见的事情是深度优先回溯搜索。请注意,我们的键和输入都有字母排序,所以我们可以稍微优化搜索。

三元树版本遵循类似于trie的原则,但是每个节点不是多个子节点,而是基本上在结构中内置了下一个字母的二叉树逻辑。搜索可以很容易地进行调整 - 每个下一个字母搜索的选项与下一个输入字母匹配或丢弃它。

当你在排序的输入字符串中运行相同的字母时,搜索中的“跳过字母”选项应该“跳到下一个不同的字母”。否则,您最终会进行重复搜索(在回溯期间) - 例如有三种不同的方法可以使用三个重复字母中的两个 - 你可以忽略第一个,第二个或第三个副本 - 你只需要检查一个案例。

优化可能在数据结构节点中有额外的细节,以帮助修剪搜索树。例如。保持子树中单词尾部的最大长度允许您检查搜索字符串的剩余部分是否包含足够的字母以继续搜索。

由于回溯,时间复杂性并不是很明显。