Мы пытаемся разбить доменное имя (s
) на любое количество слов (не только 2) из набора известных слов (words
).Рекурсия ftw!
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", ...])
Затем мысоздайте словарь английского языка:
# Assuming Linux. Word list may also be at /usr/dict/words.
# If not on Linux, grab yourself an enlish word list and insert here:
words = set(x.strip().lower() for x in open("/usr/share/dict/words").readlines())
# The above english dictionary for some reason lists all single letters as words.
# Remove all except "i" and "u" (remember a string is an iterable, which means
# that set("abc") == set(["a", "b", "c"])).
words -= set("bcdefghjklmnopqrstvwxyz")
# If there are more words we don't like, we remove them like this:
words -= set(("ex", "rs", "ra", "frobnicate"))
# We may also add words that we do want to recognize. Now the domain name
# slartibartfast4ever.co.uk will be properly counted, for instance.
words |= set(("4", "2", "slartibartfast"))
Теперь мы можем собрать все вместе:
count = {}
no_match = []
domains = ["examplecartrading.com", "examplepensions.co.uk",
"exampledeals.org", "examplesummeroffers.com"]
# Assume domains is the list of domain names ["examplecartrading.com", ...]
for domain in domains:
# Extract the part in front of the first ".", and make it lower case
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" + "достаточно".В некоторых случаях мы можем решить проблему, исключив нежелательные слова, такие как «ex» в приведенном выше примере кода.Для других, таких как «пенсии» / «ручки» + «ионы», это может быть неизбежно, и когда это происходит, мы должны предотвратить подсчет всех других слов в строке несколько раз (один раз для «пенсии» и один раздля "ручек" + "ионы").Мы делаем это, отслеживая найденные слова каждого доменного имени в наборе - наборы игнорируют дубликаты - и затем подсчитываем слова, как только все найдены.
РЕДАКТИРОВАТЬ: Реструктурироватьи добавил много комментариев.Вынуждает строки в нижний регистр, чтобы избежать пропусков из-за заглавных букв.Также добавлен список для отслеживания доменных имен, в которых не найдено ни одной комбинации слов.
РЕДАКТИРОВАНИЕ НЕКОРМАНТИЙ: Изменена функция подстроки, чтобы она лучше масштабировалась.Старая версия стала смехотворно медленной для доменных имен длиннее 16 символов или около того.Используя только четыре доменных имени, я увеличил свое время выполнения с 3,6 до 0,2 секунды!