寻找字典单词

时间:2009-08-18 04:04:03

标签: algorithm data-structures text-processing

我有很多复合字符串,它们是两个或三个英文单词的组合。

    e.g. "Spicejet" is a combination of the words "spice" and "jet"

我需要将这些单独的英语单词与这些复合字符串分开。我的字典将包含大约100000个单词。

将单个英语单词与这些复合字符串分开的效率最高。

10 个答案:

答案 0 :(得分:8)

我不确定你需要花多少时间或频率(这是一次性操作?每天?每周?)但你显然想要一个快速,加权的字典查找。

您还希望拥有一个冲突解决机制,也许是一个侧排队列来手动解决具有多种可能含义的元组的冲突。

我会调查Tries。使用一个你可以有效地找到(并加权)你的前缀,这正是你要寻找的。

你必须自己从一个好的字典来源构建试验,并在整个单词上加权节点,为自己提供一个高质量的参考机制。

这里只是头脑风暴,但是如果你知道你的数据集主要由duplet或三元组组成,你可能可以通过多个Trie查找来逃避,例如查找'Spic'然后'ejet',然后发现两个结果都有低分,放弃'Spice'和'Jet',两者之间的两个尝试都会产生良好的综合效果。

另外,我会考虑对最常见的前缀使用频率分析,直到任意或动态限制,例如:过滤'the'或'un'或'in'并相应地加权。

听起来很有趣,祝你好运!

答案 1 :(得分:4)

如果目的是在回答时找到“输入的最大可能分解”,那么如果使用某种图论,算法可能相当简单。您可以使用复合词并在每个字母前后创建带有顶点的图形。您将为字符串中的每个索引创建一个顶点,并在结束时使用一个顶点。接下来,您会在字典中找到所有合法单词的子字符串。然后,对于每个合法子字符串,将权重为1的边添加到图中,该图连接子字符串中第一个字母之前的顶点与子字符串中最后一个字母之后的顶点。最后,使用最短路径算法找到第一个和最后一个顶点之间边缘最少的路径。

伪代码是这样的:

parseWords(compoundWord)
    # Make the graph
    graph = makeGraph()
    N = compoundWord.length
    for index = 0 to N
        graph.addVertex(i)

    # Add the edges for each word
    for index = 0 to N - 1
        for length = 1 to min(N - index, MAX_WORD_LENGTH)
            potentialWord = compoundWord.substr(index, length)
            if dictionary.isElement(potentialWord)
                graph.addEdge(index, index + length, 1)

    # Now find a list of edges which define the shortest path
    edges = graph.shortestPath(0, N)

    # Change these edges back into words.
    result = makeList()
    for e in edges
        result.add(compoundWord.substr(e.start, e.stop - e.start + 1))
    return result
显然,我没有测试过这个伪代码,并且可能存在一些一对一的索引错误,并且没有任何错误检查,但基本的想法就在那里。我在学校做过类似的事情,效果很好。边创建循环为O(M * N),其中N是复合词的长度,M是字典中的最大字长或N(以较小者为准)。最短路径算法的运行时间取决于您选择的算法。最容易想到Dijkstra's。我认为它的运行时间是O(N ^ 2 * log(N)),因为可能的最大边是N ^ 2.

您可以使用任何最短路径算法。有several shortest path algorithms有各自的优点和缺点,但我猜你的情况差别不会太大。如果您没有尝试找到尽可能少的单词来分解化合物,而是想找到最可能的单词,那么您可以给出负边权重并尝试使用an algorithm that allows negative weights找到最短路径。

答案 2 :(得分:2)

在我看来,您希望将词典存储在Trie或DAWG数据结构中。

特里已将单词存储为复合词。所以“spicejet”将存储为“spice jet ”,其中*表示单词的结尾。您所要做的就是在字典中查找复合词,并跟踪您击中的词尾终止符的数量。从那里你必须尝试每个子串(在这个例子中,我们还不知道“jet”是否是一个单词,所以我们必须查看它。)

答案 3 :(得分:2)

你将如何决定如何划分事物?浏览网页,您会发现有其他含义的网址示例。

假设你没有资本继续下去,你会用这些做什么(目前我想到的是,我知道还有更多。):

PenIsland
KidsExchange
TherapistFinder

最后一个特别成问题,因为麻烦的部分是两个单词一起运行但不是复合词,当你打破它时,意思完全改变。

答案 4 :(得分:2)

所以,一句话,它是一个复合词,由另外两个英语单词组成?你可以为所有这些复合词提供某种查找表,但如果你只是检查候选人并尝试匹配英语单词,你就会得到误报。

编辑:看起来好像我将不得不提供一些例子。我想到的词包括:

accustomednesses != accustomed + nesses
adulthoods != adult + hoods
agreeabilities != agree + abilities
willingest != will + ingest
windlasses != wind + lasses
withstanding != with + standing
yourselves != yours + elves
zoomorphic != zoom + orphic
ambassadorships != ambassador + ships
allotropes != allot + ropes

这是一些试图说明问题的python代码。在磁盘上找一本字典,然后去吧:

from __future__ import with_statement

def opendict(dictionary=r"g:\words\words(3).txt"):
    with open(dictionary, "r") as f:
        return set(line.strip() for line in f)

if __name__ == '__main__':
    s = opendict()
    for word in sorted(s):
        if len(word) >= 10:
            for i in range(4, len(word)-4):
                left, right = word[:i], word[i:]
                if (left in s) and (right in s):
                    if right not in ('nesses', ):
                        print word, left, right

答案 5 :(得分:1)

在我看来,任何合理的复合词都有相对较少的子串(最小长度为2)。例如,对于“spicejet”,我得到:

'sp', 'pi', 'ic', 'ce', 'ej', 'je', 'et',
'spi', 'pic', 'ice', 'cej', 'eje', 'jet',
'spic', 'pice', 'icej', 'ceje', 'ejet',
'spice', 'picej', 'iceje', 'cejet',
'spicej', 'piceje', 'icejet',
'spiceje' 'picejet'

...... 26个子串。

因此,找到一个函数来生成所有这些函数(使用2,3,4 ... (len(yourstring) - 1)的步幅滑过字符串,然后只需检查集合或散列表中的每一个。

答案 6 :(得分:1)

Word存在可以使用trie完成,或者更简单地使用set(即哈希表)完成。给定一个合适的函数,你可以这样做:

# python-ish pseudocode
def splitword(word):
    # word is a character array indexed from 0..n-1

    for i from 1 to n-1:
        head = word[:i]  # first i characters
        tail = word[i:]  # everything else

        if is_word(head):
            if i == n-1:
                return [head]   # this was the only valid word; return it as a 1-element list
            else:
                rest = splitword(tail)
                if rest != []:   # check whether we successfully split the tail into words
                    return [head] + rest

    return []  # No successful split found, and 'word' is not a word.

基本上,只需尝试不同的断点,看看我们是否可以说话。递归意味着它将回溯直到找到成功的分割。

当然,这可能找不到你想要的分裂。你可以修改它来返回所有可能的分裂(而不仅仅是第一个找到的分裂),然后做一些加权和,或许,更喜欢常见的单词而不是不常见的单词。

答案 7 :(得分:1)

最近提出了一个类似的问题:Word-separating algorithm。如果你想限制分割的数量,你会跟踪每个元组中的分割数(所以不是一对,而是三元组)。

答案 8 :(得分:1)

这可能是一个非常困难的问题,并且没有简单的通用解决方案(可能有适用于小子集的启发式方法)。

我们在化学中正是面临这个问题,其中名称由词素的连接组成。一个例子是:

ethylmethylketone

语素是:

ethyl methyl and ketone

我们通过自动机和最大熵来解决这个问题,代码可以在Sourceforge上找到

http://www.sf.net/projects/oscar3-chem

但请注意,这需要一些工作。

我们有时会遇到歧义,并且仍在寻找报告它的好方法。

要区分penIsland和penisLand,需要特定于域的启发式方法。可能的解释将取决于所使用的语料库 - 没有任何语言问题独立于所分析的域或域。

另一个例子是字符串

weeknight

可以解析为

wee knight

week night

两者都是“正确的”,因为它们遵循“adj-noun”或“noun-noun”的形式。两者都是“有意义的”,选择哪个将取决于使用领域。在奇幻游戏中,第一个更可能,而在商业中则是后者。如果您遇到这类问题,那么拥有一个由专家注释的商定用法语料库(技术上是自然语言处理中的“黄金标准”)将是有用的。

答案 9 :(得分:0)

我会使用以下算法。

  1. 从排序的单词列表开始 拆分,以及排序列表 谢谢(词典)。

  2. 创建对象的结果列表 应该存储:剩下的字 和匹配的单词列表。

  3. 在结果列表中填入单词 分成剩余的单词。

  4. 遍历结果数组和 同时字典 - 总是增加最少的 二,以类似的方式 合并算法。这样你就可以 比较所有可能的匹配 一对通过。

  5. 任何时候找到匹配项,即a 拆分以单词开头的单词 字典单词,替换 匹配字典单词和 剩余部分在结果列表中。 你必须考虑到 可能的倍数。

  6. 任何时候剩下的部分都是空的, 你找到了最后的结果。

  7. 任何时候找不到匹配项 换句话说,“左侧” 每次增加结果 因为没有匹配指针,删除 相应的结果项。这个 单词没有匹配,也不可能 分裂。

  8. 一旦到达底部 列表,你将有一个列表 部分结果。重复循环 直到这是空的 - 转到第4点。