Как найти слово из массивов символов? - PullRequest
7 голосов
/ 17 мая 2011

Каков наилучший способ решить эту проблему:

У меня есть группа массивов с 3-4 символами внутри каждого, например:

{p,     {a,    {t,    {m,
 q,      b,     u,     n,
 r,      c      v      o
 s      }      }      }
}

У меня также есть массив словаряwords.

Какой самый лучший / быстрый способ найти, если массив символов может объединиться в одно из словарных слов?Например, приведенные выше массивы могут составлять слова:

"pat", "rat", "at", "to", "bum" (lol)
, но не "nub" или "mat""

Должен ли я пройтись по словарю, чтобы посмотреть, можно ли составить слова, или получить все комбинации из букв, а затем сравнить их со словарем

Ответы [ 4 ]

16 голосов
/ 20 мая 2011

У меня был какой-то код Scrabble, поэтому я смог собрать его вместе.Словарь, который я использовал, это sowpods (267751 слов).Приведенный ниже код читает словарь как текстовый файл с одним заглавным словом в каждой строке.

Код C #:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace SO_6022848
{
  public struct Letter
  {
    public const string Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public static implicit operator Letter(char c)
    {
      return new Letter() { Index = Chars.IndexOf(c) };
    }
    public int Index;
    public char ToChar()
    {
      return Chars[Index];
    }
    public override string ToString()
    {
      return Chars[Index].ToString();
    }
  }

  public class Trie
  {
    public class Node
    {
      public string Word;
      public bool IsTerminal { get { return Word != null; } }
      public Dictionary<Letter, Node> Edges = new Dictionary<Letter, Node>();
    }

    public Node Root = new Node();

    public Trie(string[] words)
    {
      for (int w = 0; w < words.Length; w++)
      {
        var word = words[w];
        var node = Root;
        for (int len = 1; len <= word.Length; len++)
        {
          var letter = word[len - 1];
          Node next;
          if (!node.Edges.TryGetValue(letter, out next))
          {
            next = new Node();
            if (len == word.Length)
            {
              next.Word = word;
            }
            node.Edges.Add(letter, next);
          }
          node = next;
        }
      }
    }

  }

  class Program
  {
    static void GenWords(Trie.Node n, HashSet<Letter>[] sets, int currentArrayIndex, List<string> wordsFound)
    {
      if (currentArrayIndex < sets.Length)
      {
        foreach (var edge in n.Edges)
        {
          if (sets[currentArrayIndex].Contains(edge.Key))
          {
            if (edge.Value.IsTerminal)
            {
              wordsFound.Add(edge.Value.Word);
            }
            GenWords(edge.Value, sets, currentArrayIndex + 1, wordsFound);
          }
        }
      }
    }

    static void Main(string[] args)
    {
      const int minArraySize = 3;
      const int maxArraySize = 4;
      const int setCount = 10;
      const bool generateRandomInput = true;

      var trie = new Trie(File.ReadAllLines("sowpods.txt"));
      var watch = new Stopwatch();
      var trials = 10000;
      var wordCountSum = 0;
      var rand = new Random(37);

      for (int t = 0; t < trials; t++)
      {
        HashSet<Letter>[] sets;
        if (generateRandomInput)
        {
          sets = new HashSet<Letter>[setCount];
          for (int i = 0; i < setCount; i++)
          {
            sets[i] = new HashSet<Letter>();
            var size = minArraySize + rand.Next(maxArraySize - minArraySize + 1);
            while (sets[i].Count < size)
            {
              sets[i].Add(Letter.Chars[rand.Next(Letter.Chars.Length)]);
            }
          }
        }
        else
        {
          sets = new HashSet<Letter>[] { 
          new HashSet<Letter>(new Letter[] { 'P', 'Q', 'R', 'S' }), 
          new HashSet<Letter>(new Letter[] { 'A', 'B', 'C' }), 
          new HashSet<Letter>(new Letter[] { 'T', 'U', 'V' }), 
          new HashSet<Letter>(new Letter[] { 'M', 'N', 'O' }) };
        }

        watch.Start();
        var wordsFound = new List<string>();
        for (int i = 0; i < sets.Length - 1; i++)
        {
          GenWords(trie.Root, sets, i, wordsFound);
        }
        watch.Stop();
        wordCountSum += wordsFound.Count;
        if (!generateRandomInput && t == 0)
        {
          foreach (var word in wordsFound)
          {
            Console.WriteLine(word);
          }
        }
      }
      Console.WriteLine("Elapsed per trial = {0}", new TimeSpan(watch.Elapsed.Ticks / trials));
      Console.WriteLine("Average word count per trial = {0:0.0}", (float)wordCountSum / trials);
    }

  }

}

Вот вывод при использовании данных теста:

PA
PAT
PAV
QAT
RAT
RATO
RAUN
SAT
SAU
SAV
SCUM
AT
AVO
BUM
BUN
CUM
TO
UM
UN
Elapsed per trial = 00:00:00.0000725
Average word count per trial = 19.0

И вывод при использовании случайных данных (не печатает каждое слово):

Elapsed per trial = 00:00:00.0002910
Average word count per trial = 62.2

РЕДАКТИРОВАТЬ: Я сделал это намного быстрее с двумя изменениями: Сохранениеслово в каждом терминальном узле дерева, так что его не нужно перестраивать.И сохранение входных букв в виде массива хеш-наборов вместо массива массивов, так что вызов Contains () будет быстрым.

3 голосов
/ 17 мая 2011

Вероятно, существует множество способов решения этой проблемы.

Что вас интересует, так это число каждого символа , которое у вас есть для формирования слова, и количество каждого символатребуется для каждого словарного слова.Хитрость заключается в том, как эффективно искать эту информацию в словаре.

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

В любом случае, вам, вероятно, придется попробовать всеваши возможности и проверить их по словарю.То есть, если у вас есть три массива по три значения в каждом, будет 3 ^ 3 + 3 ^ 2 + 3 ^ 1 = 39 комбинаций для проверки.Если этот процесс слишком медленный, то, возможно, вы можете вставить фильтр Блума перед словарем, чтобы быстро проверить, нет ли слова в словаре.

РЕДАКТИРОВАТЬ: В любом случае, разве это не то же самое, что Эрудит?Возможно, попробуйте Google для « алгоритм скрэббл », и вы получите несколько хороших подсказок.

1 голос
/ 24 мая 2011

Я только что сделал очень большой вложенный цикл для следующего:

for(NSString*s1 in [letterList objectAtIndex:0]{
    for(NSString*s2 in [letterList objectAtIndex:1]{
       8 more times...
    }
}

Затем я делаю двоичный поиск по комбинации, чтобы увидеть, есть ли она в словаре, и добавляю ее в массив, если она

1 голос
/ 17 мая 2011

На переформулированный вопрос можно ответить только путем генерации и тестирования.Поскольку у вас есть 4 буквы и 10 массивов, у вас есть только около 1 миллиона возможных комбинаций (10 миллионов, если вы допустите пустой символ).Вам понадобится эффективный способ их поиска, использовать BDB или какой-нибудь хэш на основе диска.

Предыдущее опубликованное решение должно также работать, вы просто более ограничены тем, какие символы вы можете выбрать на каждом этапе поиска.Также должно быть быстрее.

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