Найти подстроки, которые являются палиндромами в Java
1. обзор
В этом кратком руководстве мы рассмотрим различные подходы кfinding all substrings within a given string that are palindromes. Мы также отметим временную сложность каждого подхода.
2. Метод грубой силы
В этом подходе мы просто перебираем входную строку, чтобы найти все подстроки. Заодно проверим, является ли подстрока палиндромом:
public Set findAllPalindromesUsingBruteForceApproach(String input) {
Set palindromes = new HashSet<>();
for (int i = 0; i < input.length(); i++) {
for (int j = i + 1; j <= input.length(); j++) {
if (isPalindrome(input.substring(i, j))) {
palindromes.add(input.substring(i, j));
}
}
}
return palindromes;
}
В приведенном выше примере мы просто сравниваем подстроку с ее обратной стороной, чтобы убедиться, что это палиндром:
private boolean isPalindrome(String input) {
StringBuilder plain = new StringBuilder(input);
StringBuilder reverse = plain.reverse();
return (reverse.toString()).equals(input);
}
Конечно, мы можем легко выбратьseveral other approaches.
The time complexity of this approach is O(n^3). Хотя это может быть приемлемо для небольших входных строк, нам потребуется более эффективный подход, если мы проверяем палиндромы в больших объемах текста.
3. Подход централизации
Идея централизованного подхода состоит в том, чтобыconsider each character as the pivot and expand in both directions to find palindromes.
Мы будем расширять только в том случае, если символы слева и справа совпадают, и строка квалифицируется как палиндром. В противном случае мы продолжаем к следующему символу.
Давайте посмотрим на небольшую демонстрацию, в которой мы будем рассматривать каждого персонажа как центр палиндрома:
public Set findAllPalindromesUsingCenter(String input) {
Set palindromes = new HashSet<>();
for (int i = 0; i < input.length(); i++) {
palindromes.addAll(findPalindromes(input, i, i + 1));
palindromes.addAll(findPalindromes(input, i, i));
}
return palindromes;
}
В вышеуказанном цикле мы расширяемся в обоих направлениях, чтобы получить набор всех палиндромов по центру в каждой позиции. Мы найдем палиндромы четной и нечетной длины, дважды вызвав методfindPalindromes в цикле:
private Set findPalindromes(String input, int low, int high) {
Set result = new HashSet<>();
while (low >= 0 && high < input.length() && input.charAt(low) == input.charAt(high)) {
result.add(input.substring(low, high + 1));
low--;
high++;
}
return result;
}
The time complexity of this approach is O(n^2). Это улучшение по сравнению с нашим методом грубой силы, но мы можем добиться большего, как мы увидим в следующем разделе.
4. Алгоритм Манахера
Manacher’s algorithmfinds the longest palindromic substring in linear time. Мы будем использовать этот алгоритм, чтобы найти все подстроки, которые являются палиндромами.
Прежде чем мы углубимся в алгоритм, мы инициализируем несколько переменных.
Во-первых, мы защитим входную строку с помощью граничного символа в начале и в конце перед преобразованием полученной строки в массив символов:
String formattedInput = "@" + input + "#";
char inputCharArr[] = formattedInput.toCharArray();
Затем мы воспользуемся двумерным массивомradius с двумя строками - одна для хранения длины палиндромов нечетной длины, а другая для хранения длины палиндромов четной длины:
int radius[][] = new int[2][input.length() + 1];
Затем мы переберем входной массив, чтобы найти длину палиндрома с центром в позицииi и сохраним эту длину вradius[][]:
Set palindromes = new HashSet<>();
int max;
for (int j = 0; j <= 1; j++) {
radius[j][0] = max = 0;
int i = 1;
while (i <= input.length()) {
palindromes.add(Character.toString(inputCharArr[i]));
while (inputCharArr[i - max - 1] == inputCharArr[i + j + max])
max++;
radius[j][i] = max;
int k = 1;
while ((radius[j][i - k] != max - k) && (k < max)) {
radius[j][i + k] = Math.min(radius[j][i - k], max - k);
k++;
}
max = Math.max(max - k, 0);
i += k;
}
}
Наконец, мы пройдемся по массивуradius[][], чтобы вычислить палиндромные подстроки с центром в каждой позиции:
for (int i = 1; i <= input.length(); i++) {
for (int j = 0; j <= 1; j++) {
for (max = radius[j][i]; max > 0; max--) {
palindromes.add(input.substring(i - max - 1, max + j + i - 1));
}
}
}
Временная сложность этого подхода составляет O (n).
5. Заключение
В этой быстрой статье мы обсудили временные сложности различных подходов к поиску подстрок, которые являются палиндромами.
Как всегда, доступен полный исходный код примеровover on GitHub.