Как разбить данный текст на слова из словаря?
Это вопрос интервью. Предположим, что у вас есть строка 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 - длина самого длинного слова в словаре.
Я надеюсь, что это поможет, и я приношу свои извинения за мой плохой английский.