Comprendre l'algorithme du plus long sous-chaîne palindromique (LeetCode #5)

Cette analyse se concentre sur la résolution du problème 5 de LeetCode, "Plus Longue Sous-chaîne Palindromique", en utilisant la méthode d'expansion à partir du centre. Le code fourni est écrit en Java.

L'approche clé consiste à traiter séparément les palindromes de longueur impaire et de longueur paire.

Structure Générale du Code

class Solution {
    public String longestPalindrome(String s) {
        String currentLongest = ""; // Stocke la plus longue sous-chaîne palindromique trouvée jusqu'à présent
        for (int i = 0; i < s.length(); i++) { // Itère sur chaque caractère comme point de départ potentiel
            
            // Partie 1 : Traitement des palindromes de longueur impaire
            // ... (lignes 5-14)
            
            // Partie 2 : Traitement des palindromes de longueur paire
            // ... (lignes 15-24)
        }
        return currentLongest; // Retourne le résultat final
    }
}

Analyse Détaillée de la Logique

A. Traitement des Palindromes de Longueur Impaire

Correspond aux lignes 5 à 14 :

// Palindrome impair
int left = i - 1; 
int right = i + 1;
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
    left--;
    right++;
}
// Vérifie si le palindrome trouvé est plus long que le courant
if (currentLongest.length() < right - left - 1) {
    // Met à jour la plus longue sous-chaîne
    currentLongest = s.substring(left + 1, right); 
}

  • Initialisation : left = i - 1, right = i + 1. Le caractère central implicite est s.charAt(i). La comparaison commence donc par les caractères immédiatement à gauche et à droite du centre.
  • Boucle d'expansion : Tant que les indices left et right sont valides et que les caractères correspondants sont égaux, on étend le palindrome vers l'extérieur (left--, right++).
  • Calcul de la Longueur : Après la sortie de la boucle, left et right pointent vers les premiers caractères non correspondants (ou hors limites). La sous-chaîne palindromique valide se situe entre left + 1 et right - 1 (inclus). La longueur est donc (right - 1) - (left + 1) + 1, ce qui se simplifie en right - left - 1.
  • Extraction de la Sous-chaîne : s.substring(left + 1, right). La méthode substring(début, fin) en Java inclut l'indice début mais exclut l'indice fin. Pour inclure le caractère à right - 1, on utilise right comme indice de fin.

B. Traitement des Palindromes de Longueur Paire

Correspond aux lignes 15 à 24 :

// Palindrome pair
left = i;
right = i + 1;
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
    left--;
    right++;
}
// Vérifie si le palindrome trouvé est plus long que le courant
if (currentLongest.length() < right - left - 1) {
    // Met à jour la plus longue sous-chaîne
    currentLongest = s.substring(left + 1, right);
}

  • Initialisation : left = i, right = i + 1. Le centre implicite est l'espace entre le caractère i et le caractère i+1. La comparaison commence directement avec ces deux caractères.
  • Comportement en cas d'inégalité : Si s.charAt(i) est différent de s.charAt(i+1) (par exemple, 'a' et 'b' dans "abc"), la boucle while ne s'exécute pas. La longueur calculée sera 0, et currentLongest ne sera pas mis à jour, ce qui est le comportement attendu.
  • Logique d'expansion et d'extraction : La logique est identique à celle du traitement des palindromes impairs une fois que la boucle while commence.

Explication des Notes Manuscrites (Capture d'Écran)

La note manuscrite "substring est avant fermé puis ouvert donc il faut +1" explique l'utilisation de (left + 1, right) pour s.substring.

  • Raisonnement :
  1. Après la boucle while, left pointe au premier caractère non correspondant à gauche (ou -1).
  2. right pointe au premier caractère non correspondant à droite (ou s.length()).
  3. La plage du palindrome valide est théoriquement [left+1, right-1].
  4. La méthode substring(début, fin) en Java utilise une plage semi-ouverte [début, fin).
  5. Pour couvrir l'indice right-1, l'indice de fin à passer à substring doit être (right - 1) + 1, c'est-à-dire right.
  • Conclusion : Le "+1" mentionné dans la note fait référence à l'ajustement nécessaire de l'indice de fin pour s'adapter à la sémantique "avant fermé, après ouvert" de la méthode substring.

Point d'Attention (Suggestion d'Optimisation)

Bien que fonctionnel, le code présente une légère redondance :

Dans la partie traitant les palindromes impairs :

int left = i - 1;
int right = i + 1;

Cette initialisation saute s.charAt(i). Si l'on considère qu'un caractère unique est un palindrome valide, cette logique s'appuie sur la condition if pour potentiellement mettre à jour currentLongest. Si la boucle while ne s'exécute pas, la longueur calculée sera (i+1) - (i-1) - 1 = 1. L'appel s.substring(i, i+1) extrait correctement le caractère central. La logique est auto-cohérente.

Problème Potentiel : Dans la partie traitant les palindromes pairs, si s.charAt(i) != s.charAt(i+1), la boucle ne s'exécute pas. Les indices restent left=i, right=i+1. La longueur calculée est (i+1) - i - 1 = 0. La condition currentLongest.length() < 0 est fausse, donc currentLongest n'est pas mis à jour. Ceci est également auto-cohérent.

Résumé

Ce code est une implémentation classique de la méthode d'expansion à partir du centre :

  1. Itération Double : Deux ensembles d'initialisations pour left et right couvrent systématiquement les centres de type "caractère" (longueur impaire) et "espace entre caractères" (longueur paire).
  2. Gestion des Limites : La boucle while gère nativement les débordements d'indices.
  3. Extraction Précise : L'utilisation de substring(left + 1, right) exploite la caractéristique "avant fermé, après ouvert" pour extraire la sous-chaîne correcte.

L'importance de traiter séparément les cas impairs et pairs est cruciale. Sans la section traitant les palindromes pairs (lignes 15-24), une entrée comme "cbbd" ne permettrait pas de trouver le palindrome "bb".

Puorquoi cette double tentative sans if ?

L'approche est de tenter les deux scénarios (centre impair, centre pair) pour chaque position i sans vérification préalable.

"Pour chaque position i, je vais d'abord essayer de l'étendre comme centre d'un palindrome impair, puis je vais essayer de l'étendre comme centre d'un palindrome pair (entre i et i+1)."

  • Scénario Palinrdome Impair (ex: "aba") :
  • La logique impair trouve "aba".
  • La logique pair (entre 'b' et 'a') échoue à la première comparaison ('b' != 'a'). La boucle ne s'exécute pas, aucun changement n'est apporté.
  • Scénario Palindrome Pair (ex: "abba") :
  • La logique impair (centre sur le premier 'b') échoue ('a' != 'b'). Elle ne trouve qu'un palindrome de longueur 1.
  • La logique pair (entre les deux 'b') réussit l'expansion et trouve "abba".

Pourquoi pas de if ?

La boucle while agit comme un filtre naturel. Si une tentative d'expansion est invalide dès le départ (par exemple, si s.charAt(i) != s.charAt(i+1) pour le cas pair), la boucle ne s'exécute pas et cette tentative n'a aucun impact.

C'est comme essayer deux clés sur une serrure sans savoir de quel type elle est. La bonne clé fonctionenra, la mauvaise échouera silencieusement.

L'ajout d'un if rendrait le code plus complexe (gestion des limites de i+1) et potentiellement moins robuste, sans apporter de bénéfice clair. L'approche actuelle garantit une couverture complète et une simplicité d'implémentation.

}

Étiquettes: Java Algorithme chaîne de caractères palindrome leetcode

Publié le 10 juillet à 23h03