Прежде всего, давайте посмотрим на ограничения на проблему.Вы хотите сохранить список слов для игры в структуре данных, которая эффективно поддерживает проблему «анаграммы».То есть, учитывая "стойку" из n букв, все слова из n или меньше букв в списке слов, которые можно сделать из этой стойки.список слов будет составлять около 400 тыс. слов, и, вероятно, он будет содержать от одного до десяти мегабайт строковых данных в несжатом виде.
Три - это классическая структура данных, используемая для решения этой проблемы, поскольку она сочетает в себе эффективность памяти и поискэффективность.Со списком слов около 400 тыс. Слов разумной длины вы сможете сохранить это в памяти.(В отличие от решения, подобного b-дереву, в котором большая часть дерева хранится на диске, потому что он слишком велик, чтобы помещаться в память сразу.)
Три обычно не более чем26-арное дерево (при условии, что вы используете латинский алфавит), где у каждого узла есть буква и один дополнительный бит на каждом узле, который говорит, является ли это концом слова.
Итак, давайте набросаем структуру данных:
class TrieNode
{
char Letter;
bool IsEndOfWord;
List<TrieNode> children;
}
Это, конечно, просто набросок;вы, вероятно, захотите сделать так, чтобы у них были правильные методы доступа и конструкторы и тому подобное.Кроме того, возможно, плоский список не является лучшей структурой данных;может быть, какой-то словарь лучше.Мой совет - сначала запустите его, а затем измерьте его производительность, а если это неприемлемо, то поэкспериментируйте с внесением изменений, чтобы улучшить его производительность.
Вы можете начать с пустого дерева:
TrieNode root = new TrieNode('^', false, new List<TrieNode>());
То есть это «корневой» узел дерева, который представляет начало слова.
Как добавить слово «AA», первое слово в словаре скрэббл?Что ж, сначала сделайте узел для первой буквы:
root.Children.Add('A', false, new List<TrieNode>());
ОК, наш три теперь
^
|
A
Теперь добавьте узел для второй буквы:
root.Children[0].Children.Add(new trieNode('A', true, new List<TrieNode>()));
Наш трек теперь
^
|
A
|
A$ -- we notate the end of word flag with $
Отлично.Теперь предположим, что мы хотим добавить AB.У нас уже есть узел для «A», поэтому добавьте к нему узел «B $»:
root.Children[0].Children.Add(new trieNode('B', true, new List<TrieNode>());
, и теперь у нас есть
^
|
A
/ \
A$ B$
Продолжайте в том же духе.Конечно, вместо того, чтобы писать «root.Children [0] ...», вы напишите цикл, который ищет триан, чтобы увидеть, существует ли нужный вам узел, и если нет, создайте его.
Toсохранить ваше дерево на диске - честно говоря, я бы просто сохранил список слов в виде простого текстового файла и перестроил дерево, когда вам нужно.Это не должно занять более 30 секунд или около того, и тогда вы можете повторно использовать дерево в памяти.Если вы хотите сохранить файл в каком-то формате, который больше похож на файл, вам не составит труда найти формат сериализации.
Чтобы найти три на предмет соответствия стойки, идея состоит в том, чтобы исследовать каждую его часть, но исключить области, в которых стойка не может совпадать.Если у вас нет «А» на стойке, нет необходимости отключать любой «А» узел.Я набросал алгоритм поиска в вашем предыдущем вопросе.
У меня есть реализация постоянного функционального стиля, о котором я давно хотел написать, но так и не нашел его.Если я в конце концов опубликую это, я обновлю этот вопрос.