Поиск и выделение слов в тексте (Алгоритм)

Есть база людей, каждая запись может содержать неограниченное кол-во синонимов (имена написанные на разных языках, клички и т.д.). Все записи (как синонимы, так и названия) абсолютно уникальны. По этим данным строиться индекс таблица (назовем ее source) которая может быть представлена так:
name VARCHAR(128),
person_id INT(10),
UNIQUE KEY name (name)

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

Мой велосипед:
  • На базе source строим индекс в sphinx состоящий из триграмм, отбрасываем записи длиною менее 2 символов.
  • В тексте выбираем все слова которые начинаются с заглавной буквы, игнорируя двухсимвольные. Если слова идут подряд, то считаем их как одно (ФИО). С опаской относимся к словам в начале предложений.
  • Превращаем их в триграммы и кидаем запрос сфинксу.
  • Выбираем из результата одно самое подходящее и далее выделяем его в тексте.

Подразумеваю что порог ошибок в данном случае будет около 5%, что в принципе допустимо (ошибочное можно исправить вручную).

В общем, хотел бы узнать: Какие недочеты есть в моем алгоритме и какие могут быть предложения по улучшению? Какие существуют решения похожих задач (наверняка есть, но что-то я туплю, или красивых слов не знаю, поэтому гугл не дает ничего вразумительного)? Как бы вы решили эту задачу?
  • Вопрос задан
  • 7624 просмотра
Пригласить эксперта
Ответы на вопрос 3
@ffriend
Для начала понадобится отдельная функция или утилита stem(), которая для любого слова вернёт его основу (именно это и делают поисковые движки, в т.ч. Sphinx и Lucene, откуда её и можно достать и использовать в своих целях). Также не помешает функция tokenize(), разбивающая

Если такая функция есть. Проходим по всем словам из словаря (source), а из получившихся основ строим trie, в котором листьями будут соответствующие person_id. Имена из 2 и более слов транслируются в n-граммы соответсвующих основ, разделённые пробелами. Т.е. имя «Иванова Анна Михайловна» будет транслировано во что-то вроде «иванов анн михайловн» (использовать lowercase-фильтр или нет — это уже зависит от приложения и текста: если текст грамотный, то не надо, если «из интернетов», то лучше всё-таки использовать).

Дальше токенизируем текст, стеммим каждое слово и последовательно ищем их в нашем trie. Можно искать сразу триграммы, можно подтягивать второе и третье слово по необходимости (если есть частичное совпадение в trie).

Если trie реализовывать лень, можно заменить их на hash map'ы, но тогда имена из нескольких слов лучше хранить в виде списка, а hash map сделать вложенным (первое слово в hash map'е верхнего уровня указывает на другой hash map, хронящий все возможные вторые слова для указанного первого слова; получается этакое дерево из hash map'ов).

Если считать, что поиск по trie/в hash map'е выполняется за O(1), то весь алгоритм отработает за O(n), где n — количество слов в тексте. При этом не придётся индексировать весь текст, а только структуру для хранения основ имён (индексирование в Lucene/Sphinx, вообще говоря, не самая быстрая операция, а размер индекса около 20-30% от текста, так что не факт, что влезет в память; естественно, я предполагаю, что количество имён меньше размера текста).

Summary:

1. Применить stem() ко всем именам.
2. Сохранить слова в структуру с быстрым поиском (trie/hash map).
3. Токенизировать текст, применить stem() ко всем полученным словам.
4. Пройтись по списку получившихся слов, при этом ища их в структуре с именами.
5. Profit.
Ответ написан
grossws
@grossws
Умные слова: словоформа, нормализация, компьютерная лингвистика, стеммер Портера, aot, mystem. Для начала, надеюсь хватит.

И кстати, «хватит извращений, откапывайте стюардессу». Посмотрите на apache solr (сделан на lucene).
Ответ написан
winbackgo
@winbackgo Автор вопроса
Не совсем мне понятно где в trie место для person_id.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы