Encontre substrings que são palíndromos em Java
1. Visão geral
Neste tutorial rápido, vamos passar por diferentes abordagens parafinding all substrings within a given string that are palindromes. Também observaremos a complexidade de tempo de cada abordagem.
2. Abordagem da força bruta
Nesta abordagem, vamos simplesmente iterar sobre a string de entrada para encontrar todas as substrings. Ao mesmo tempo, verificaremos se a substring é um palíndromo ou não:
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;
}
No exemplo acima, apenas comparamos a substring com seu reverso para ver se é um palíndromo:
private boolean isPalindrome(String input) {
StringBuilder plain = new StringBuilder(input);
StringBuilder reverse = plain.reverse();
return (reverse.toString()).equals(input);
}
Claro, podemos escolher facilmente entreseveral other approaches.
The time complexity of this approach is O(n^3). Embora isso possa ser aceitável para pequenas strings de entrada, precisaremos de uma abordagem mais eficiente se estivermos verificando palíndromos em grandes volumes de texto.
3. Abordagem de centralização
A ideia na abordagem de centralização éconsider each character as the pivot and expand in both directions to find palindromes.
Expandiremos apenas se os caracteres nos lados esquerdo e direito corresponderem, qualificando a string para ser um palíndromo. Caso contrário, continuamos para o próximo caractere.
Vamos ver uma rápida demonstração em que consideraremos cada personagem como o centro de um palíndromo:
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;
}
No loop acima, expandimos nas duas direções para manter o conjunto de todos os palíndromos centralizados em cada posição. Encontraremos palíndromos de comprimento par e ímpar chamando o métodofindPalindromes duas vezes no loop:
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). Esta é uma melhoria em relação à nossa abordagem de força bruta, mas podemos fazer ainda melhor, como veremos na próxima seção.
4. Algoritmo de Manacher
Manacher’s algorithmfinds the longest palindromic substring in linear time. Usaremos esse algoritmo para encontrar todas as substrings que são palíndromos.
Antes de mergulharmos no algoritmo, inicializaremos algumas variáveis.
Primeiro, protegeremos a string de entrada com um caractere de limite no início e no final antes de converter a string resultante em uma matriz de caracteres:
String formattedInput = "@" + input + "#";
char inputCharArr[] = formattedInput.toCharArray();
Em seguida, usaremos uma matriz bidimensionalradius com duas linhas - uma para armazenar os comprimentos dos palíndromos de comprimento ímpar e a outra para armazenar os comprimentos dos palíndromos de comprimento par:
int radius[][] = new int[2][input.length() + 1];
Em seguida, vamos iterar sobre a matriz de entrada para encontrar o comprimento do palíndromo centralizado na posiçãoi e armazenar esse comprimento emradius[][]:
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;
}
}
Finalmente, vamos percorrer a matrizradius[][] para calcular as substrings palindrômicas centralizadas em cada posição:
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));
}
}
}
A complexidade de tempo dessa abordagem é O (n).
5. Conclusão
Neste artigo rápido, discutimos as complexidades de tempo de diferentes abordagens para encontrar substrings que são palíndromos.
Como sempre, o código-fonte completo dos exemplos está disponívelover on GitHub.