我们尝试将域名(s
)从一组已知单词(words
)中分割成任意数量的单词(不仅仅是2个)。递归万岁!
def substrings_in_set(s, words):
if s in words:
yield [s]
for i in range(1, len(s)):
if s[:i] not in words:
continue
for rest in substrings_in_set(s[i:], words):
yield [s[:i]] + rest
这个迭代器函数首先会返回它所调用的字符串,如果该字符串在 words
中存在。然后它会将该字符串按所有可能的方式分成两部分。如果第一部分不在 words
中,它会尝试下一个分割点。如果在其中,它会将第一部分添加到所有对第二部分调用自身的结果中(第二部分可能为空,例如 ["example", "cart", ...])
接着我们构建英语词典:
words = set(x.strip().lower() for x in open("/usr/share/dict/words").readlines())
words -= set("bcdefghjklmnopqrstvwxyz")
words -= set(("ex", "rs", "ra", "frobnicate"))
words |= set(("4", "2", "slartibartfast"))
现在我们可以把事情放在一起:
count = {}
no_match = []
domains = ["examplecartrading.com", "examplepensions.co.uk",
"exampledeals.org", "examplesummeroffers.com"]
for domain in domains:
name = domain.partition(".")[0].lower()
found = set()
for split in substrings_in_set(name, words):
found |= set(split)
for word in found:
count[word] = count.get(word, 0) + 1
if not found:
no_match.append(name)
print count
print "No match found for:", no_match
结果: {'ions': 1, 'pens': 1, 'summer': 1, 'car': 1, 'pensions': 1, 'deals': 1, 'offers': 1, 'trading': 1, 'example': 4}
使用set
来包含英语词典可快速进行成员检查。 -=
从集合中删除项目,|=
将其添加到其中。
使用all
函数和生成器表达式可以提高效率,因为all
在第一个False
时返回。
有些子字符串可能是有效的单词,无论作为整体还是拆分,例如“example” / “ex” +“ample”。对于某些情况,我们可以通过排除不需要的单词来解决问题,例如上面代码示例中的“ex”。对于其他情况,例如“pensions” / “pens” +“ions”,则可能无法避免,当发生这种情况时,我们需要防止字符串中的所有其他单词被多次计数(一次用于“pensions”,一次用于“pens”+“ions”)。我们通过在每个域名的找到的单词集合中跟踪找到的单词 - 集合忽略重复项 - 然后在找到所有单词后计算单词次数来实现此目的。
编辑:重新组织并添加了大量注释。强制字符串小写以避免因大小写而错过。还添加了一个列表来跟踪没有匹配单词组合的域名。
NECROMANCY EDIT:更改子字符串函数,使其更具可扩展性。旧版本对于长度超过16个字符左右的域名变得非常缓慢。使用上面的四个域名,我将自己的运行时间从3.6秒提高到0.2秒!