Найти подстроки, которые являются палиндромами в Java

Найти подстроки, которые являются палиндромами в 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];

Затем мы переберем входной массив, чтобы найти длину палиндрома с центром в позициии сохраним эту длину в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.