Trouver des sous-chaînes qui sont des palindromes en Java

Trouver des sous-chaînes qui sont des palindromes en Java

1. Vue d'ensemble

Dans ce rapide didacticiel, nous allons passer par différentes approches definding all substrings within a given string that are palindromes. Nous noterons également la complexité temporelle de chaque approche.

2. Approche de la force brute

Dans cette approche, nous allons simplement parcourir la chaîne d'entrée pour trouver toutes les sous-chaînes. En même temps, nous vérifierons si la sous-chaîne est un palindrome ou non:

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;
}

Dans l'exemple ci-dessus, nous comparons simplement la sous-chaîne à son inverse pour voir s'il s'agit d'un palindrome:

private boolean isPalindrome(String input) {
    StringBuilder plain = new StringBuilder(input);
    StringBuilder reverse = plain.reverse();
    return (reverse.toString()).equals(input);
}

Bien sûr, nous pouvons facilement choisir parmiseveral other approaches.

The time complexity of this approach is O(n^3). Bien que cela puisse être acceptable pour les petites chaînes d'entrée, nous aurons besoin d'une approche plus efficace si nous recherchons des palindromes dans de grands volumes de texte.

3. Approche de la centralisation

L'idée dans l'approche de centralisation est deconsider each character as the pivot and expand in both directions to find palindromes.

Nous ne développerons que si les caractères à gauche et à droite correspondent, qualifiant la chaîne de palindrome. Sinon, nous passons au caractère suivant.

Voyons une démonstration rapide dans laquelle nous considérerons chaque personnage comme le centre d'un palindrome:

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;
}

Dans la boucle ci-dessus, nous développons dans les deux sens pour centrer l'ensemble des palindromes à chaque position. Nous trouverons des palindromes de longueur paire et impaire en appelant la méthodefindPalindromes deux fois dans la boucle:

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). C'est une amélioration par rapport à notre approche de la force brute, mais nous pouvons faire encore mieux, comme nous le verrons dans la section suivante.

4. Algorithme de Manacher

Manacher’s algorithmfinds the longest palindromic substring in linear time. Nous utiliserons cet algorithme pour rechercher toutes les sous-chaînes qui sont des palindromes.

Avant de plonger dans l'algorithme, nous allons initialiser quelques variables.

Tout d'abord, nous allons protéger la chaîne d'entrée avec un caractère de délimitation au début et à la fin avant de convertir la chaîne résultante en un tableau de caractères:

String formattedInput = "@" + input + "#";
char inputCharArr[] = formattedInput.toCharArray();

Ensuite, nous utiliserons un tableau bidimensionnelradius avec deux lignes - l'une pour stocker les longueurs des palindromes de longueur impaire et l'autre pour stocker les longueurs des palindromes de longueur paire:

int radius[][] = new int[2][input.length() + 1];

Ensuite, nous allons parcourir le tableau d'entrée pour trouver la longueur du palindrome centré à la positionet stocker cette longueur enradius[][]:

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;
    }
}

Enfin, nous allons parcourir le tableauradius[][] pour calculer les sous-chaînes palindromiques centrées à chaque position:

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));
        }
    }
}

La complexité temporelle de cette approche est O (n).

5. Conclusion

Dans cet article rapide, nous avons discuté des complexités temporelles de différentes approches pour trouver des sous-chaînes qui sont des palindromes.

Comme toujours, le code source complet des exemples est disponibleover on GitHub.