Word Pattern Finder

时间:2017-06-17 10:35:06

标签: algorithm language-agnostic pattern-matching wildcard

问题:查找模式后面的所有单词(独立于用于定义模式的实际符号)。

几乎与此网站的内容相同:http://design215.com/toolbox/wordpattern.php

  

输入以下模式:ABCCDE
  这会找到像“血腥”这样的词,   “小猫”和“山谷”。上面的模式不会找到像这样的单词   “茴香”或“嬉皮士”,因为这需要图案   ABCCBE。

请注意:我需要一个版本的算法,即使使用ABCCDE模式也可以找到像“茴香”或“嬉皮士”这样的词。

为了进一步复杂化,可以在搜索模式中的任何位置添加已知字符,例如: cBBX (其中 c 是已知字符)将会产生 cees,coof,cook,cool ......

到目前为止我做了什么:我发现这个答案(Pattern matching for strings independent from symbols)几乎完美地解决了我的问题,但如果我为每个需要比较的单词分配一个整数,我会遇到两个问题。

第一个是我可以使用的唯一数字的数量。例如,如果模式是 XYZABCDEFG ,则等效数字模式将是 1 2 3 4 5 6 7 8 9 然后? 10 ?考虑我将使用数字0来表示已知字符(例如, aBe - > 010 - > 10)。使用十六进制数字将进一步移动问题,但不会解决它。

第二个问题是模式的最大长度:Java中的Long长度为19位,我的模式不需要限制(尽管我认为不存在包含20个不同字符的单词)。 / p>

为了解决这些问题,我可以将模式的每个数字存储在一个数组中,但随后它变成了一个数组到数组的比较,而不是一个整数比较,因此需要花费更多的时间来计算。

作为旁注:根据所使用的算法,哪种数据结构最适合存储字典?我正在考虑使用哈希映射,将每个单词转换为其数字模式等价物(假设没有已知字符)并使用此数字作为哈希值(当然,会有很多冲突)。这样,搜索将首先匹配数字模式,然后扫描结果以查找在正确位置具有已知字符的所有单词(如果存在于原始搜索模式中)。

此外,字典不是静态的:可以添加和删除单词。

修改

这个答案(https://stackoverflow.com/a/44604329/4452829)效果相当好而且速度很快(在匹配模式之前测试相同的长度)。唯一的问题是我需要一个版本的算法,即使使用ABCCDE模式也能找到像“茴香”或“嬉皮士”这样的词。

我已经实现了一种检查已知字符的方法。

编辑2:

好的,通过检查模式中的每个字符是否大于或等于当前单词中的相应字符(标准化为临时模式),我差不多完成了:它正确匹配搜索模式 ABCA 使用 ABBA 一词,它正确地忽略了 ABAC 这个词。剩下的最后一个问题是,如果(例如)模式是ABBA,它将匹配单词ABAA,这是不正确的。

编辑3:

嗯,不是很漂亮,但似乎有效(我正在使用Python,因为它可以快速编写代码)。此外,搜索模式可以是任何符号序列,使用小写字母作为固定字符,其他所有字符作为通配符;也没有必要以抽象模式转换每个单词。

 def match(pattern, fixed_chars, word):
    d = dict()
    if len(pattern) != len(word):
        return False
    if check_fixed_char(word, fixed_chars) is False:
        return False
    for i in range(0, len(pattern)):
        cp = pattern[i]
        cw = word[i]
        if cp in d:
            if d[cp] != cw:
                return False
        else:
            d[cp] = cw
        if cp > cw:
            return False
    return True

2 个答案:

答案 0 :(得分:1)

很久以前我写了一个基于相同概念解决密码的程序(生成单词模式,使“小猫”和“山谷”都映射到“abccde”。

我的技术确实涉及按模式生成一种单词索引。

核心抽象函数如下所示:

#!python
#!/usr/bin/env python
import string

def abstract(word):
    '''find abstract word pattern
       dog or cat -> abc, book or feel -> abbc
    '''
    al = list(string.ascii_lowercase)
    d = dict
    for i in word:
        if i not in d:
            d[i] = al.pop(0)
    return ''.join([d[i] for i in word])

从那里建立我们的索引非常容易。假设我们有一个像 / usr / share / dict / words 这样的文件(常见于类Unix系统,包括MacOS X和Linux):

#!/usr/bin/python
words_by_pattern = dict()
words = set()
with open('/usr/share/dict/words') as f:
    for each in f:
        words.add(each.strip().lower())

for each in sorted(words):
    pattern = abstract(each)
    if pattern not in words_by_pattern:
        words_by_pattern[pattern] = list()
    words_by_pattern[pattern].append(each)

...在我的笔记本电脑上花费不到两秒钟就可以获得大约234,000个“单词”(尽管您可能希望为您的应用程序使用更精确或受限制的单词列表)。

此时另一个有趣的技巧是找到最独特的模式(返回尽可能少的单词)。我们可以创建一个模式直方图:

histogram = [(len(words_by_pattern[x]),x) for x in words_by_pattern.keys()]
histogram.sort()

我发现这给了我:

8077    abcdef
7882    abcdefg
6373    abcde
6074    abcdefgh
3835    abcd
3765    abcdefghi
1794    abcdefghij
1175    abc
1159    abccde
925     abccdef

请注意,abc,abcd和abcde都在前十名。换句话说,单词最常见的字母模式包括所有在3到10个字符中没有重复的字母模式。

您还可以查看直方图的直方图。换句话说,有多少模式只显示一个单词:例如aabca只匹配“eerie”而aabcb只匹配“llama”。有超过48,000种模式只有一个匹配的单词,而近六千只有两个单词,依此类推。

注意:我不使用数字;我使用字母来创建模式映射。

我不知道这对你的项目是否有帮助;但这是非常简单的代码片段。 (他们故意冗长)。

答案 1 :(得分:0)

这可以通过使用正则表达式轻松实现。 例如,以下模式匹配任何具有ABCCDE模式的单词:

(?:([A-z])(?!\1)([A-z])(?!\1|\2)([A-z])(?=\3)([A-z])(?!\1|\2|\3|\5)([A-z])(?!\1|\2|\3|\5|\6)([A-z]))

这个匹配ABCCBE

(?:([A-z])(?!\1)([A-z])(?!\1|\2)([A-z])(?=\3)([A-z])(?=\2)([A-z])(?!\1|\2|\3|\5|\6)([A-z]))

要涵盖上述两种模式,您可以使用:

(?:([A-z])(?!\1)([A-z])(?!\1|\2)([A-z])(?=\3)([A-z])(?(?=\2)|(?!\1\2\3\5))([A-z])(?!\1|\2|\3|\5|\6)([A-z]))

走这条道路,您的挑战将是使用您使用的字母表示法生成上述正则表达式模式。 请注意,如果需要不区分大小写,则可能需要在使用i正则表达式时使用rx = re.compile(r'(?<!\d)(?<!\b\w\w)\.(?!\d)') str = 'I am content. I am another content. I am 10.2 content. I am St. Content.' str = rx.split(str) print(str) 正则表达式标志。

有关更多正则表达式信息,请查看:
offline
Look-around