Вы можете легко создать случай, когда выполнение программы занимает как минимум экспоненциальное время.Давайте просто возьмем слово aaa...aaab
, где a
повторяется n
раз.Словарь будет содержать только два слова, a
и aa
.
b
. В конце убедитесь, что функция никогда не найдет совпадение и, следовательно, никогда не завершится преждевременно.
На каждом words
выполнение, будут созданы два рекурсивных вызова: с suffix(s, 1)
и suffix(s, 2)
.Следовательно, время выполнения увеличивается как числа Фибоначчи: t(n) = t(n - 1) + t(n - 2)
.(Вы можете проверить это, вставив счетчик.) Таким образом, сложность определенно не является полиномиальной.(и это даже не наихудший вариант)
Но вы можете легко улучшить свое решение с помощью Memoization .Обратите внимание, что вывод функции words
зависит только от одного: с какой позиции в исходной строке мы начинаем.То есть, если у нас есть строка abcdefg
и вызывается words(5)
, не имеет значения, как именно составлена abcde
(как ab+c+de
или a+b+c+d+e
или что-то еще).Таким образом, нам не нужно каждый раз пересчитывать words("fg")
.
В примитивной версии это можно сделать следующим образом
public static boolean words(String s, Set<String> dictionary) {
if (processed.contains(s)) {
// we've already processed string 's' with no luck
return false;
}
// your normal computations
// ...
// if no match found, add 's' to the list of checked inputs
processed.add(s);
return false;
}
PS Тем не менее, я призываю вас изменить words(String)
до words(int)
.Таким образом, вы сможете сохранять результаты в массиве и даже преобразовывать весь алгоритм в DP (что значительно упростит его).
edit 2
Поскольку я неМногое, кроме работы, вот решение DP (динамическое программирование).Та же идея, что и выше.
String s = "peachpie1";
int n = s.length();
boolean[] a = new boolean[n + 1];
// a[i] tells whether s[i..n-1] can be composed from words in the dictionary
a[n] = true; // always can compose empty string
for (int start = n - 1; start >= 0; --start) {
for (String word : dictionary) {
if (start + word.length() <= n && a[start + word.length()]) {
// check if 'word' is a prefix of s[start..n-1]
String test = s.substring(start, start + word.length());
if (test.equals(word)) {
a[start] = true;
break;
}
}
}
}
System.out.println(a[0]);