Поиск слов Эрудит с подстановочными знаками - PullRequest
6 голосов
/ 14 сентября 2011

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

В настоящее время я создаю мобильное веб-приложение, используяC #, MySQL, HTML5 и Javascript.Приложение будет использоваться, чтобы помочь пользователям найти возможные слова для игры во время игр типа Scrabble.

Проблема, с которой я столкнулся: Как получить нужные слова из базы данных MySQL, содержащей словарь из письма пользователяinput?

Подробнее: - Пользователи могут вводить любое количество букв, а также использовать подстановочные знаки (обозначающие любую букву).- Если пользователь вводит «TEST», результат не может содержать слова с более чем 1 E и S и слова с более чем 2 T, результат с «TESTER» будет плохим.- Результат не может содержать слов с большим количеством букв, чем введено.

ОБНОВЛЕНИЕ: Кажется, Три - это решение моей проблемы, предложенное Эриком Липпертом здесь .Проблема в том, что я новичок и в C #, и в MySQL, поэтому вот несколько дополнительных вопросов:

  1. Как мне создать Trie из моего словаря MySQL?(400 000 слов +)
  2. Как сохранить Trie для быстрого и будущего доступа?
  3. Как получить доступ к Trie и извлечь из него слова с помощью C #?

Большое спасибо за помощь!

Ответы [ 3 ]

23 голосов
/ 14 сентября 2011

Как получить правильные слова из базы данных MySQL, содержащей словарь из ввода букв пользователя?

Вы не делаете. Таблица реляционных баз данных не является подходящей структурой данных для решения этой проблемы так эффективно, как вам нужно.

Вместо этого вы строите структуру trie из словаря (или, если вы действительно любитель, вы создаете dawg - ориентированный ациклический граф слов - что-то вроде сжатого дерева.)

После того, как у вас есть trie / dawg, становится очень недорогим тестировать каждое слово в словаре для данной стойки, потому что вы можете "обрезать" целые огромные ветви словаря, которые стойка не может матч.

Давайте рассмотрим небольшой пример. Предположим, у вас есть словарь «OP, OPS, OPT, OPTS, POT, POTS, SOP, SOPS, STOP, STOPS». Из этого вы строите этот файл: (Узлы со знаком $ - это те, которые отмечены как «слово может заканчиваться здесь») .

           ^root^
           /  |  \
         O    P    S
         |    |   / \
         P$   O  O   T   
        / \   |  |   |
       T$  S$ T$ P$  O
       |      |  |   |
       S$     S$ S$  P$
                     |
                     S$

а у вас стойка "OPS" - что вы делаете?

Сначала вы говорите: "Могу ли я спуститься по ветке О?" Да, ты можешь. Так что теперь проблема заключается в сопоставлении «PS» с ответвлением O. Можете ли вы пойти вниз P филиал? Да. У него есть маркер конца слова? Да, так что OP это совпадение. Теперь проблема заключается в сопоставлении буквы «S» с веткой OP. Можете ли вы пойти вниз по ветке Т? Нет. Можете ли вы пойти вниз по ветке S? Да. Теперь у вас есть пустая стойка, и вы должны сопоставить ее с веткой OPS. У него есть маркер конца слова? Да! Так что OPS тоже подходит. Теперь вернемся к корню.

Можете ли вы пойти вниз по ветви P? Да. Теперь проблема состоит в том, чтобы сопоставить ОС с веткой P. Спуститесь по ветке PO и сопоставьте S - что не получается. Возврат к корню.

И снова вы видите, как это происходит. В конце концов мы спускаемся по ветке SOP и находим конец слова в SOP, поэтому «SOP» соответствует этой стойке. Мы не спускаемся по ветке ST, потому что у нас нет T.

Мы перепробовали все возможные слова в словаре и обнаружили, что OP, OPS и SOP все совпадают. Но нам никогда не приходилось исследовать OPTS, POTS, STOP или STOPS, потому что у нас не было T.

Вы видите, как эта структура данных делает ее очень эффективной? Как только вы определили, что на стойке нет букв, составляющих начало слова, вам не нужно исследовать любые словарные слова, начинающиеся с этого начала. Если у вас есть PO, но нет T, вам не нужно исследовать POTSHERD или POTATO или POTASH или POTLATCH или POTABLE; все эти дорогие и бесплодные поиски уходят очень быстро.

Адаптировать систему для работы с «дикими» тайлами довольно просто; если у вас есть OPS ?, тогда просто запустите алгоритм поиска 26 раз, на OPSA, OPSB, OPSC ... Это должно быть достаточно быстро, чтобы сделать это 26 раз дешевле (или сделать это 26 x 26 раз, если у вас два пробела). )

Это основной алгоритм, используемый профессиональными программами Scrabble AI, хотя, конечно, им приходится иметь дело с такими вещами, как положение платы, управление стойкой и т. Д., Что несколько усложняет алгоритмы. Эта простая версия алгоритма будет достаточно быстрой, чтобы генерировать все возможные слова в стойке.

Не забывайте, что, конечно, вам нужно только вычислить trie / dawg один раз , если словарь не меняется со временем. Построение дерева из словаря может занять много времени, поэтому вы можете сделать это один раз , а затем найти способ сохранить дерево на диске в форме, пригодной для его восстановления. быстро с диска.

Вы можете оптимизировать использование памяти, создав DAWG из дерева. Обратите внимание, как много повторений, потому что в английском много слов конец одинаковы, так же как много слов начинаются одинаково. Trie делает большую работу по совместному использованию узлов в начале, но паршивая работа по их совместному использованию в конце. Например, вы можете заметить, что шаблон «S $ без детей» встречается крайне часто, и превратить дерево в:

           ^root^
          / |  \
        O   P    S
        |   |   / \
        P$  O  O   T   
       /  \ |  |   |
      T$  | T$ P$  O
      |    \ | |   |
       \    \| /   P$
        \    |/    |
         \   |    /
          \  |   /  
           \ |  /
            \| /  
             |/
             |       
             S$

Сохранение целой кучи узлов. И затем вы можете заметить, что два слова теперь заканчиваются на O-P $ -S $, а два слова заканчиваются на T $ -S $, так что вы можете сжать его до:

           ^root^
           / | \
          O  P  S
          |  | / \
          P$ O \  T   
         /  \|  \ |
         |   |   \|
         |   |    O
         |   T$   |
          \  |    P$
           \ |   /
            \|  /  
             | /
             |/   
             S$

И теперь у нас есть минимальная DAWG для этого словаря.

Дальнейшее чтение:

http://dl.acm.org/citation.cfm?id=42420

http://archive.msdn.microsoft.com/dawg1

http://www.gtoal.com/wordgames/scrabble.html

0 голосов
/ 14 сентября 2011

Вот как я мог бы решить эту проблему (если, конечно, у вас есть контроль над БД, и вы можете изменять таблицы / добавлять таблицы или даже контролировать исходную загрузку БД).

Мое решение будет использовать 2 таблицы -> одна таблица будет просто списком всех возможных комбинаций букв из вашего словаря с компонентными буквами, отсортированными по алфавиту. (IE TEST будет ESTT, TESTER будет ERSTT, DAD будет ADD).

Во второй таблице будет каждое слово и ссылка на ключ для таблицы.

Таблица первая - LetterInWord

Index Letters
1     ESTT
2     ESTTER
3     EST
4     ADD
5     APST

В первой таблице вы вводите слова буквы в алфавитном порядке - тест становится estt

Таблица 2 - Слова

Index LetterInWordIndex  Word
1     1                  TEST
2     2                  TESTER
3     3                  SET
4     4                  ADD
5     4                  DAD
6     5                  SPAT
7     5                  PAST               

В таблице 2 вы вставляете слово с соответствующим словом и индексной ссылкой.

Это будет отношение один ко многим -> Одна запись в таблице LetterInWord может иметь несколько записей в таблице слов

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

Затем в поиске вы выбираете все «Письма» из LetterInWord, где Letters = значение и объединяете таблицу слов. Ваш вывод в одном запросе представляет собой список всех слов, которые содержат только эти буквы

Теперь для групповых символов: Скажи, что мои вводные буквы EST * Запомни длину - 4 Удалите подстановочные знаки - вы получите EST (убедитесь, что вы сортируете это в алфавитном порядке) Теперь просмотрите все случаи, когда Letters содержит EST и Letters Length <= 4, соединенные в таблице слов </p>

Это вернет TEST, REST, SET и т. Д.

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

0 голосов
/ 14 сентября 2011

Это будет очень сложно сделать, если у вас есть только словарь.Если у вас есть возможность создать новую таблицу или новые столбцы, я бы:

Создать таблицу со столбцом для слова плюс 26 столбцов (по одному для каждой буквы). Запустите сохраненный процесс proc / backend, которыйподсчитывает вхождения каждой буквы в слове и помещает их в соответствующий столбец.

Затем (без учета подстановочных знаков) вы можете сделать

Выбрать слово из словаря, где tcount <= 2 и ecount <= 1 и scount <= 1 </p>

для подстановочных знаков, которые вы можете использовать, и длина <= number_of_letters </p>

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

Все остальное будет чрезвычайно медленным во время запроса

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...