Как разбить данный текст на слова из словаря?

Это вопрос интервью. Предположим, что у вас есть строка text и dictionary (набор строк). Как вы разбиваете text на подстроки, чтобы каждая подстрока находилась в dictionary.

Например, вы можете разбить "thisisatext" на ["this", "is", "a", "text"] с помощью /usr/share/dict/words.

Я считаю, что откат может решить эту проблему (в псевдо-Java):

void solve(String s, Set<String> dict, List<String> solution) {
   if (s.length == 0)
      return
   for each prefix of s found in dict
      solve(s without prefix, dict, solution + prefix)
}

List<String> solution = new List<String>()
solve(text, dict, solution)

Имеет ли смысл? Вы бы оптимизировали шаг поиска префиксов в словаре? Какие структуры данных вы бы порекомендовали?

Ответы

Ответ 1

Это решение предполагает существование структуры данных Trie для словаря. Далее, для каждого node в Trie принимает следующие функции:

  • node.IsWord(): возвращает true, если путь к этому node - это слово
  • node.IsChild(char x): возвращает true, если существует дочерний элемент с меткой x
  • node.GetChild(char x): возвращает дочерний элемент node с меткой x
Function annotate( String str, int start, int end, int root[], TrieNode node):
i = start
while i<=end:
    if node.IsChild ( str[i]):
        node = node.GetChild( str[i] )
        if node.IsWord():
            root[i+1] = start
        i+=1
    else:
        break;

end = len(str)-1
root = [-1 for i in range(len(str)+1)]
for start= 0:end:
    if start = 0 or root[start]>=0:
        annotate(str, start, end, root, trieRoot)

index  0  1  2  3  4  5  6  7  8  9  10  11
str:   t  h  i  s  i  s  a  t  e  x  t
root: -1 -1 -1 -1  0 -1  4  6 -1  6 -1   7

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

Сложность по времени - это O (nk), где n - длина строки, а k - длина самого длинного слова в словаре.

PS: Я принимаю следующие слова в словаре: this, is, a, text, ate.

Ответ 2

В этом блоге

Ответ 3

Подход 1 - Trie выглядит очень близко. Создайте три слова в английском словаре. Это трехэтажное здание - одноразовая стоимость. После того, как trie будет построено, ваш string можно легко сопоставить буквой. если в любой момент вы сталкиваетесь с листом в trie, вы можете предположить, что нашли слово, добавьте это в список и перейдите к вашему обходу. Пройдите обход до тех пор, пока вы не достигнете конца вашего string. Список выводится.

Сложность времени для поиска - O (word_length).

Космическая сложность - O (charsize * word_length * no_words). Размер вашего словаря.

Подход 2 - Я слышал о Suffix Trees, никогда не использовал их, но он может быть полезен здесь.

Подход 3 - более педантичный и паршивый вариант. вы уже предложили это.

Вы могли бы попробовать наоборот. Запуск через dict - проверка соответствия подстроки. Здесь я предполагаю, что ключи в dict являются words английского словаря /usr/share/dict/words. Таким образом, код psuedo выглядит примерно так:

(list) splitIntoWords(String str, dict d)
{
    words = []
    for (word in d)
    {
        if word in str
            words.append(word);
    }
    return words;
}

Сложность - O (n), проходящая через весь dict + O (1) для соответствия подстроки.

Пространство - худший случай O (n), если len(words) == len(dict)

Как отмечали другие, для этого требуется откат.

Ответ 4

Вы можете решить эту проблему, используя Динамическое программирование и Hashing.

Вычислить хэш каждого слова в словаре. Используйте функцию хэша, которая вам больше нравится. Я бы использовал нечто вроде (a1 * B ^ (n - 1) + a2 * B ^ (n - 2) +... + an * B ^ 0)% P, где a1a2... an - строка, n - длина строки, B - основание многочлена, а P - большое простое число. Если у вас есть хэш-значение строки a1a2... an, вы можете рассчитать хэш-значение строки a1a2... ana (n + 1) в постоянное время: (hashValue (a1a2... an) * B + a (n + 1))% P.

Сложность этой части - O (N * M), где N - количество слов в словаре, а M - длина самого длинного слова в словаре.

Затем используйте функцию DP, например:

   bool vis[LENGHT_OF_STRING];
   bool go(char str[], int length, int position)
   {
      int i;

      // You found a set of words that can solve your task.
      if (position == length) {
          return true;
      }

      // You already have visited this position. You haven't had luck before, and obviously you won't have luck this time.
      if (vis[position]) {
         return false;
      }
      // Mark this position as visited.
      vis[position] = true;

      // A possible improvement is to stop this loop when the length of substring(position, i) is greater than the length of the longest word in the dictionary.
      for (i = position; position < length; i++) {
         // Calculate the hash value of the substring str(position, i);
         if (hashValue is in dict) {
            // You can partition the substring str(i + 1, length) in a set of words in the dictionary.
            if (go(i + 1)) {
               // Use the corresponding word for hashValue in the given position and return true because you found a partition for the substring str(position, length).
               return true;
            }
         }
      }

      return false;
   }

Сложность этого алгоритма - O (N * M), где N - длина строки, а M - длина самого длинного слова в словаре или O (N ^ 2), в зависимости, если вы закодировали улучшение или нет.

Таким образом, общая сложность алгоритма будет: O (N1 * M) + O (N2 * M) (или O (N2 ^ 2)), где N1 - количество слов в словаре, M - длина самого длинного слова в словаре, а N2 - длина строки).

Если вы не можете придумать хорошую хеш-функцию (там, где нет никакого столкновения), другое возможное решение - использовать Tries или Patricia trie (если размер обычного trie очень большой) (я не мог " t сообщения для этих тем, потому что моя репутация недостаточно высока, чтобы разместить более двух ссылок). Но вы используете это, сложность вашего алгоритма будет O (N * M) * O (время, необходимое для поиска слова в trie), где N - длина строки, а M - длина самого длинного слова в словаре.

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