Наивный алгоритм не даст хороших результатов при применении к реальным данным. Вот 20-строчный алгоритм, который использует относительную частоту слов для получения точных результатов для текста реального слова.
(Если вы хотите получить ответ на свой исходный вопрос, в котором не используется частота слов, вам необходимо уточнить, что именно означает «самое длинное слово»: лучше ли иметь слово из 20 букв и десять слов из 3 букв, или лучше иметь пять слов из 10 букв? Как только вы определитесь с точным определением, вам просто нужно изменить определение линии, wordcost
чтобы отразить предполагаемое значение.)
Идея
Наилучший способ продолжить - смоделировать распределение выпуска. Хорошее первое приближение - это предположить, что все слова распределены независимо. Тогда вам нужно только знать относительную частоту всех слов. Разумно предположить, что они следуют закону Ципфа, то есть слово с рангом n в списке слов имеет вероятность примерно 1 / ( n log N ), где N - количество слов в словаре.
После того, как вы зафиксировали модель, вы можете использовать динамическое программирование для определения положения пробелов. Наиболее вероятное предложение - это предложение, которое максимизирует произведение вероятности каждого отдельного слова, и его легко вычислить с помощью динамического программирования. Вместо прямого использования вероятности мы используем стоимость, определяемую как логарифм обратной величины вероятности, чтобы избежать переполнения.
Код
from math import log
# Build a cost dictionary, assuming Zipf's law and cost = -math.log(probability).
words = open("words-by-frequency.txt").read().split()
wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words))
maxword = max(len(x) for x in words)
def infer_spaces(s):
"""Uses dynamic programming to infer the location of spaces in a string
without spaces."""
# Find the best match for the i first characters, assuming cost has
# been built for the i-1 first characters.
# Returns a pair (match_cost, match_length).
def best_match(i):
candidates = enumerate(reversed(cost[max(0, i-maxword):i]))
return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates)
# Build the cost array.
cost = [0]
for i in range(1,len(s)+1):
c,k = best_match(i)
cost.append(c)
# Backtrack to recover the minimal-cost string.
out = []
i = len(s)
while i>0:
c,k = best_match(i)
assert c == cost[i]
out.append(s[i-k:i])
i -= k
return " ".join(reversed(out))
который вы можете использовать с
s = 'thumbgreenappleactiveassignmentweeklymetaphor'
print(infer_spaces(s))
Результаты
Я использую этот быстрый и грязный словарь из 125 тысяч слов, который я собрал из небольшого подмножества Википедии.
Раньше: thumbgreenappleactiveassignment еженедельная метафора.
После: большой палец зеленое яблоко активное задание еженедельная метафора.
Раньше: есть масса текстовой информации людей, комментарии, которые анализируются из HTML, но в них есть ограниченное количество символов.
После: есть масса текстовой информации о комментариях людей, которая анализируется из html, но в них нет разделенных символов, например, метафора активного назначения большого пальца зеленого яблока еженедельная метафора, по-видимому, в строке есть зеленое яблоко большого пальца и т.д. У меня также есть большой словарь для спросите, разумно ли это слово, и какой самый быстрый способ извлечения?
Раньше: была мрачная и буря, ночью шел дождь, за исключением случайных периодов времени, когда его проверяли яростным порывом ветра, который вымывал улицы, вдали от холода, грохотал по крышам домов и яростно волновался тент пламяпламени против шлема.
После: это была темная и бурная ночь, дождь лил ливнем, за исключением периодических периодов, когда его сдерживал сильный порыв ветра, захлестнувший улицы, потому что именно в Лондоне наша сцена грохочет по крышам домов и яростно волнует людей. скудное пламя ламп, боровшихся с тьмой.
Как видите, он по сути безупречен. Самая важная часть - убедиться, что ваш список слов был обучен корпусу, аналогичному тому, с которым вы действительно столкнетесь, иначе результаты будут очень плохими.
Оптимизация
Реализация потребляет линейное количество времени и памяти, поэтому она достаточно эффективна. Если вам нужно дополнительное ускорение, вы можете построить дерево суффиксов из списка слов, чтобы уменьшить размер набора кандидатов.
Если вам нужно обработать очень большую последовательную строку, было бы разумно разделить строку, чтобы избежать чрезмерного использования памяти. Например, вы можете обрабатывать текст блоками по 10000 символов плюс поле в 1000 символов с каждой стороны, чтобы избежать граничных эффектов. Это сведет использование памяти к минимуму и почти наверняка не повлияет на качество.