Поиск словарных слов

У меня много сложных строк, которые представляют собой комбинацию из двух или трех английских слов.

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

Мне нужно отделить эти отдельные английские слова от таких сложных строк. Мой словарь будет состоять примерно из 100000 слов.

Что было бы самым эффективным, чтобы я мог отделить отдельные английские слова от таких сложных строк.

10 ответов

Я не уверен, сколько времени или частоты вам нужно делать (это разовая операция "ежедневно?"?), но вы, очевидно, захотите быстро найти взвешенный словарь.

Вам также понадобится механизм разрешения конфликтов, возможно, боковая очередь для ручного разрешения конфликтов в кортежах, которые имеют несколько возможных значений.

Я бы посмотрел на Забавывает. Используя один, вы можете эффективно находить (и вес) свои префиксы, которые именно вы ищите.

Вам нужно будет самостоятельно создавать Tries из хорошего источника словаря и весить узлы на полных словах, чтобы обеспечить себе качественный механизм для справки.

Просто мозговой штурм здесь, но если вы знаете, что ваш набор данных состоит в основном из дуплексов или триплетов, вы, вероятно, можете уйти с несколькими поисками Trie, например, глядя вверх "Spic", а затем "ejet", а затем обнаружив, что оба результата имеют низкий балл, отказаться от "Spice" и "Jet", где оба Tries будут давать хороший комбинированный результат между ними.

Также я хотел бы рассмотреть использование частотного анализа на наиболее распространенных префиксах до произвольного или динамического предела, например. фильтруя '' или 'un' или 'in' и взвешивая их соответственно.

Похоже на забавную проблему, удачи!


Если цель состоит в том, чтобы найти "наибольший возможный разрыв для ввода", как вы ответили, тогда алгоритм может быть довольно простым, если вы используете какую-либо теорию графов. Вы принимаете составное слово и составляете граф с вершиной до и после каждой буквы. У вас будет вершина для каждого индекса в строке и одна за концом. Затем вы найдете все легальные слова в словаре, которые являются подстроками сложного слова. Затем для каждой законной подстроки добавьте ребро с весом 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.

Вы можете использовать любой алгоритм кратчайшего пути. Существуют несколько алгоритмов кратчайшего пути, которые имеют свои различные сильные и слабые стороны, но я предполагаю, что для вашего случая разница не будет слишком значительной, Если вместо того, чтобы пытаться найти наименьшее количество слов, чтобы разбить соединение, вы хотели бы найти максимально возможное, тогда вы дадите краям отрицательные веса и попытайтесь найти кратчайший путь с помощью алгоритм, который позволяет отрицательные веса.


И как вы решите, как разделить вещи? Просмотрите веб-страницы, и вы найдете примеры URL-адресов, которые, как оказалось, имеют другие значения.

Предполагая, что у вас не было столиц, что бы вы делали с ними (о которых сейчас приходит в голову, я знаю, что их больше.):

PenIsland
KidsExchange
TherapistFinder

Последнее особенно проблематично, потому что неприятная часть состоит из двух слов, которые работают вместе, но не являются составным словом, значение полностью изменяется, когда вы его нарушаете.


Мне кажется, что вы хотите сохранить словарь в Trie или структуре данных DAWG.

В Trie уже хранятся слова как составные слова. Таким образом, "spicejet" будет храниться как "spicejet", где * обозначает конец слова. Все, что вам нужно сделать, это найти сложное слово в словаре и отслеживать, сколько терминальных терминаторов вы нажмете. Оттуда вы должны попробовать каждую подстроку (в этом примере мы еще не знаем, является ли "струя" словом, поэтому нам нужно будет это посмотреть).


Итак, учитывая слово, это сложное слово, состоящее из двух других английских слов? У вас может быть какая-то таблица поиска для всех таких составных слов, но если вы просто изучите кандидатов и попытаетесь сопоставить английские слова, вы получите ложные срабатывания.

Изменить: выглядит так, как будто мне нужно будет привести некоторые примеры. Слова, о которых я думал, включают:

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


Это может быть очень сложной проблемой, и нет простого общего решения (могут быть эвристики, которые работают для небольших подмножеств).

Мы сталкиваемся именно с этой проблемой в химии, где имена составлены путем конкатенации морфем. Пример:

<p> где морфемы:</p>
<pre class="prettyprint linenums******* ****** and ketone

Мы решаем это с помощью автоматов и максимальной энтропии, а код доступен на Sourceforge

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

но следует предупредить, что это займет определенную работу.

Мы иногда сталкиваемся с двусмысленностью и все еще находим хороший способ сообщить об этом.

Для разграничения между penIsland и penisLand потребуются эвристики, специфичные для домена. Вероятная интерпретация будет зависеть от используемого тела - никакая лингвистическая проблема не зависит от исследуемого домена или доменов.

В качестве другого примера строка

weeknight

можно проанализировать как

wee knight

или

week night

Оба являются "правильными" в том, что они подчиняются форме "adj-существительное" или "существительное-существительное". Оба делают "смысл", а выбранный будет зависеть от области использования. В фэнтезийной игре первое более вероятно и в торговле последнее. Если у вас есть проблемы такого рода, тогда будет полезно иметь состав согласованного использования, который был аннотирован экспертами (технически "золотой стандарт" в обработке естественного языка).


Недавно был задан аналогичный вопрос: алгоритм разделения слов. Если вы хотите ограничить количество разделов, вы будете отслеживать количество разделов в каждом из кортежей (поэтому вместо пары, тройной).


Мне кажется, что существует относительно небольшое количество подстрок (минимальная длина 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), а затем просто проверьте каждый из них в таблице набора или хэша.


Существование слов может быть выполнено с помощью trie или более просто с набором (т.е. хеш-таблицей). С учетом подходящей функции вы можете:

# 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.

В принципе, попробуйте разные точки разрыва, чтобы увидеть, можем ли мы делать слова. Рекурсия означает, что она вернется в исходное состояние, пока не будет найден успешный раскол.

Конечно, это может не найти разделение, которое вы хотите. Вы можете изменить это, чтобы вернуть все возможные расщепления (а не только первые найденные), затем сделать какую-то взвешенную сумму, возможно, предпочитать общие слова по необычным словам.


Я бы использовал следующий алгоритм.

  • Начните с отсортированного списка слов для разделения, и отсортированный список отклоненные слова (словарь).

  • Создать список результатов который должен хранить: оставшееся слово и список совпадающих слов.

  • Заполните список результатов словами для разделения как оставшихся слов.

  • Пройдите через массив результатов и словарь одновременно - всегда увеличивая наименьшее из два, аналогично алгоритм слияния. Таким образом, вы можете сравнить все возможные соответствия пар за один проход.

  • Каждый раз, когда вы находите совпадение, т.е. словом слова, которое начинается с словаря, замените совпадающее слово слова и оставшаяся часть в списке результатов. Вы должны учитывать возможные кратные.

  • В любой момент, когда оставшаяся часть пуста, вы нашли окончательный результат.

  • В любое время, когда вы не найдете "левая сторона", другими словами, каждый раз, когда вы увеличиваете результат указатель из-за отсутствия соответствия, удалить соответствующий элемент результата. Эта слово не имеет совпадений и не может быть Раскол.

  • Как только вы дойдете до списков, у вас будет список частичные результаты. Повторите цикл пока это не пусто - перейдите к пункту 4.

licensed under cc by-sa 3.0 with attribution.