web-dev-qa-db-fra.com

Trouver toutes les sous-chaînes qui sont des palindromes

Si l'entrée est 'abba', les palindromes possibles sont a, b, b, a, bb, abba.
Je comprends que déterminer si string est un palindrome est facile. Ce serait comme:

public static boolean isPalindrome(String str) {
 int len = str.length();
 for(int i=0; i<len/2; i++) {
     if(str.charAt(i)!=str.charAt(len-i-1) {
         return false;
     }
 return true;  
}

Mais quel est le moyen efficace de trouver des sous-chaînes palindromes?

20
Himanshu Yadav

Cela peut être fait dans O(n), en utilisant algorithme de Manacher .

L'idée principale est une combinaison de programmation dynamique et (comme d'autres l'ont déjà dit) du calcul de la longueur maximale du palindrome avec le centre dans une lettre donnée. 


Ce que nous voulons vraiment calculer, c'est radius du plus long palindrome, pas la longueur. Le radius est simplement length/2 ou (length - 1)/2 (pour les palindromes de longueur impaire).

Après avoir calculé le rayon de palindrome pr à la position donnée i, nous utilisons déjà radiuses calculés pour rechercher des palindromes dans la plage [i - pr ; i]. Cela nous permet (parce que les palindromes sont, eh bien, les palindromes) d’ignorer le calcul ultérieur de radiuses pour la plage [i ; i + pr].

Pendant que nous cherchons dans la plage [i - pr ; i], il existe quatre cas de base pour chaque position i - k (où k est dans 1,2,... pr):

  • pas de palindrome (radius = 0) à i - k
    (cela signifie aussi radius = 0 à i + k aussi)
  • inner palindrome, ce qui signifie qu'il se range dans la plage
    (cela signifie que radius à i + k est identique à i - k)
  • outer palindrome, ce qui signifie qu'il ne rentre pas dans la plage
    (cela signifie que radius à i + k est cut down pour tenir dans la plage, c’est-à-dire parce que i + k + radius > i + pr nous réduisons radius à pr - k)
  • sticky palindrome, ce qui signifie i + k + radius = i + pr
    (dans ce cas, nous devons rechercher un rayon potentiellement plus grand en i + k)

Une explication complète et détaillée serait plutôt longue. Qu'en est-il des exemples de code? :)

J'ai trouvé l'implémentation C++ de cet algorithme par le professeur de polonais, Mgr Jerzy Wałaszek.
J'ai traduit des commentaires en anglais, ajouté quelques commentaires et simplifié un peu pour être plus facile à saisir.
Jetez un oeil ici .


Remarque: en cas de problème pour comprendre pourquoi il s’agit de O(n), essayez de regarder de cette façon:
après avoir trouvé radius (appelons-le r) à une position, nous devons itérer sur les éléments r, mais nous pouvons ignorer le calcul pour r éléments en avant. Par conséquent, le nombre total d'éléments itérés reste le même. 

29
Michał Rybak

Vous pourriez peut-être parcourir le caractère moyen potentiel (palindromes de longueur impaire) et les points intermédiaires entre les caractères (palindromes de longueur égale) et les prolonger jusqu'à ce que vous ne puissiez plus vous déplacer (les caractères suivants ne correspondent pas). 

Cela permettrait d'économiser beaucoup de calculs lorsqu'il n'y a pas beaucoup de palidromes dans la chaîne. Dans ce cas, le coût serait de O(n) pour des chaînes de palidrome clairsemées. 

Pour les entrées denses en palindrome, il s'agirait de O (n ^ 2), car chaque position ne peut pas être étendue davantage que la longueur du tableau/2. Évidemment, cela est encore moins vers les extrémités du tableau. 

  public Set<String> palindromes(final String input) {

     final Set<String> result = new HashSet<>();

     for (int i = 0; i < input.length(); i++) {
         // expanding even length palindromes:
         expandPalindromes(result,input,i,i+1);
         // expanding odd length palindromes:
         expandPalindromes(result,input,i,i);
     } 
     return result;
  }

  public void expandPalindromes(final Set<String> result, final String s, int i, int j) {
      while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) {
            result.add(s.substring(i,j+1));
            i--; j++;
      }
  }
17
Valentin Ruano

Ainsi, chaque lettre distincte est déjà un palindrome - vous avez donc déjà N + 1 palindromes, où N est le nombre de lettres distinctes (plus une chaîne vide). Vous pouvez le faire en un seul passage - O (N). 

Maintenant, pour les palindromes non triviaux, vous pouvez tester chaque point de votre chaîne comme un centre de palindrome potentiel - grandir dans les deux sens - ce que Valentin Ruano a suggéré. 
Cette solution prendra O (N ^ 2) puisque chaque test est O(N) et le nombre de "centres" possibles est également O(N) - la center est soit une lettre, soit espace entre deux lettres, toujours comme dans la solution de Valentin.

Notez qu'il existe également une solution O(N) à votre problème, basée sur Manacher's algoritm (l'article décrit "le plus long palindrome", mais un algorithme pourrait être utilisé pour les compter tous)

9
kiruwka

Je viens d’arriver avec ma propre logique qui aide à résoudre ce problème . Bon codage .. :-)

System.out.println("Finding all palindromes in a given string : ");
        subPal("abcacbbbca");

private static void subPal(String str) {
        String s1 = "";
        int N = str.length(), count = 0;
        Set<String> palindromeArray = new HashSet<String>();
        System.out.println("Given string : " + str);
        System.out.println("******** Ignoring single character as substring palindrome");
        for (int i = 2; i <= N; i++) {
            for (int j = 0; j <= N; j++) {
                int k = i + j - 1;
                if (k >= N)
                    continue;
                s1 = str.substring(j, i + j);
                if (s1.equals(new StringBuilder(s1).reverse().toString())) {
                    palindromeArray.add(s1);
                }
            }

        }
        System.out.println(palindromeArray);
        for (String s : palindromeArray)
            System.out.println(s + " - is a palindrome string.");
        System.out.println("The no.of substring that are palindrome : "
                + palindromeArray.size());
    }
Output:-
Finding all palindromes in a given string : 
Given string : abcacbbbca
******** Ignoring single character as substring palindrome ********
[cac, acbbbca, cbbbc, bb, bcacb, bbb]
cac - is a palindrome string.
acbbbca - is a palindrome string.
cbbbc - is a palindrome string.
bb - is a palindrome string.
bcacb - is a palindrome string.
bbb - is a palindrome string.
The no.of substring that are palindrome : 6
6
Bala

Je suggère de construire à partir d'un scénario de base et de développer jusqu'à ce que vous ayez tous les palindomes.

Il existe deux types de palindromes: les pairs et les impairs. Je n'ai pas trouvé comment gérer les deux de la même manière, alors je vais le séparer.

1) Ajouter toutes les lettres simples

2) Avec cette liste, vous avez tous les points de départ pour vos palindromes. Exécutez chacun de ces deux éléments pour chaque index de la chaîne (ou 1 -> longueur-1 car vous avez besoin d'au moins 2 longueurs):

findAllEvenFrom(int index){
  int i=0;
  while(true) {
    //check if index-i and index+i+1 is within string bounds

    if(str.charAt(index-i) != str.charAt(index+i+1)) 
      return; // Here we found out that this index isn't a center for palindromes of >=i size, so we can give up

    outputList.add(str.substring(index-i, index+i+1));
    i++;
  }
}
//Odd looks about the same, but with a change in the bounds.
findAllOddFrom(int index){
  int i=0;
  while(true) {
    //check if index-i and index+i+1 is within string bounds

    if(str.charAt(index-i-1) != str.charAt(index+i+1)) 
      return;

    outputList.add(str.substring(index-i-1, index+i+1));
    i++;
  }
}

Je ne suis pas sûr que cela aide le Big-O pour votre exécution, mais cela devrait être beaucoup plus efficace que d'essayer chaque sous-chaîne. Le cas le plus défavorable serait une chaîne de la même lettre qui pourrait être pire que le plan "trouver chaque sous-chaîne", mais avec la plupart des entrées, il coupera la plupart des sous-chaînes palindrome.

1
Eric Barr
public class PolindromeMyLogic {

static int polindromeCount = 0;

private static HashMap<Character, List<Integer>> findCharAndOccurance(
        char[] charArray) {
    HashMap<Character, List<Integer>> map = new HashMap<Character, List<Integer>>();
    for (int i = 0; i < charArray.length; i++) {
        char c = charArray[i];
        if (map.containsKey(c)) {
            List list = map.get(c);
            list.add(i);
        } else {
            List list = new ArrayList<Integer>();
            list.add(i);
            map.put(c, list);
        }
    }
    return map;
}

private static void countPolindromeByPositions(char[] charArray,
        HashMap<Character, List<Integer>> map) {
    map.forEach((character, list) -> {
        int n = list.size();
        if (n > 1) {
            for (int i = 0; i < n - 1; i++) {
                for (int j = i + 1; j < n; j++) {
                    if (list.get(i) + 1 == list.get(j)
                            || list.get(i) + 2 == list.get(j)) {
                        polindromeCount++;
                    } else {
                        char[] temp = new char[(list.get(j) - list.get(i))
                                + 1];
                        int jj = 0;
                        for (int ii = list.get(i); ii <= list
                                .get(j); ii++) {
                            temp[jj] = charArray[ii];
                            jj++;
                        }
                        if (isPolindrome(temp))
                            polindromeCount++;
                    }

                }
            }
        }
    });
}

private static boolean isPolindrome(char[] charArray) {
    int n = charArray.length;
    char[] temp = new char[n];
    int j = 0;
    for (int i = (n - 1); i >= 0; i--) {
        temp[j] = charArray[i];
        j++;
    }
    if (Arrays.equals(charArray, temp))
        return true;
    else
        return false;
}

public static void main(String[] args) {
    String str = "MADAM";
    char[] charArray = str.toCharArray();
    countPolindromeByPositions(charArray, findCharAndOccurance(charArray));
    System.out.println(polindromeCount);
}
}

Essayez ceci. C'est ma propre solution.

0
Bhavani

Le code consiste à trouver toutes les sous-chaînes distinctes qui sont en palindrome. Voici le code que j'ai essayé. Cela fonctionne bien. 

import Java.util.HashSet;
import Java.util.Set;

public class SubstringPalindrome {

    public static void main(String[] args) {
        String s = "abba";
        checkPalindrome(s);
}

public static int checkPalindrome(String s) {
    int L = s.length();
    int counter =0;
    long startTime = System.currentTimeMillis();
    Set<String> hs = new HashSet<String>();
    // add elements to the hash set
    System.out.println("Possible substrings: ");
    for (int i = 0; i < L; ++i) {
      for (int j = 0; j < (L - i); ++j) {
          String subs = s.substring(j, i + j + 1);
            counter++;
            System.out.println(subs);
            if(isPalindrome(subs))
                hs.add(subs);
      }
    }
    System.out.println("Total possible substrings are "+counter);
    System.out.println("Total palindromic substrings are "+hs.size());
    System.out.println("Possible palindromic substrings: "+hs.toString());
    long endTime = System.currentTimeMillis();
    System.out.println("It took " + (endTime - startTime) + " milliseconds");
    return hs.size();
}
public static boolean isPalindrome(String s) {
    if(s.length() == 0 || s.length() ==1)
        return true;
    if(s.charAt(0) ==  s.charAt(s.length()-1))
        return isPalindrome(s.substring(1, s.length()-1));
    return false;
}

}

SORTIE:

Substrations possibles:

Le nombre total de sous-chaînes possibles est 10

4 sous-chaînes palindromiques totales

Sous-chaînes palindromiques possibles: [bb, a, b, abba]

Il a fallu 1 millisecondes

0
user8778562

J'ai essayé le code suivant et cela fonctionne bien pour les cas Il gère également les caractères individuels aussi

Peu de cas ayant abouti:

abaaa --> [aba, aaa, b, a, aa] 
geek  --> [g, e, ee, k] 
abbaca --> [b, c, a, abba, bb, aca] 
abaaba -->[aba, b, abaaba, a, baab, aa] 
abababa -->[aba, babab, b, a, ababa, abababa, bab] 
forgeeksskeegfor --> [f, g, e, ee, s, r, eksske, geeksskeeg, 
                      o, eeksskee, ss, k, kssk]

Code 

static Set<String> set = new HashSet<String>(); 
static String DIV = "|";

public static void main(String[] args) {
    String str = "abababa";
    String ext = getExtendedString(str);

    // will check for even length palindromes
    for(int i=2; i<ext.length()-1; i+=2) {
        addPalindromes(i, 1, ext);
    }
    // will check for odd length palindromes including individual characters
    for(int i=1; i<=ext.length()-2; i+=2) {
        addPalindromes(i, 0, ext);
    }
    System.out.println(set);
}

/*
 * Generates extended string, with dividors applied
 * eg: input = abca
 * output = |a|b|c|a|
 */
static String getExtendedString(String str) {
    StringBuilder builder = new StringBuilder();
    builder.append(DIV);
    for(int i=0; i< str.length(); i++) {
        builder.append(str.charAt(i));
        builder.append(DIV);

    }
    String ext = builder.toString();
    return ext;
}

/*
 * Recursive matcher
 * If match is found for palindrome ie char[mid-offset] = char[mid+ offset]
 * Calculate further with offset+=2
 * 
 * 
 */
static void addPalindromes(int mid, int offset, String ext) {
    // boundary checks
    if(mid - offset <0 || mid + offset > ext.length()-1) {
        return;
    }
    if (ext.charAt(mid-offset) == ext.charAt(mid+offset)) {
        set.add(ext.substring(mid-offset, mid+offset+1).replace(DIV, ""));
        addPalindromes(mid, offset+2, ext);
    }
}

J'espère que c'est bien

0
hemantvsn
    // Maintain an Set of palindromes so that we get distinct elements at the end
    // Add each char to set. Also treat that char as middle point and traverse through string to check equality of left and right char


static int palindrome(String str) {

    Set<String> distinctPln = new HashSet<String>();
    for (int i=0; i<str.length();i++) {
        distinctPln.add(String.valueOf(str.charAt(i)));
        for (int j=i-1, k=i+1; j>=0 && k<str.length(); j--, k++) {
            // String of lenght 2 as palindrome
            if ( (new Character(str.charAt(i))).equals(new Character(str.charAt(j)))) { 
                distinctPln.add(str.substring(j,i+1));
            }
            // String of lenght 2 as palindrome
            if ( (new Character(str.charAt(i))).equals(new Character(str.charAt(k)))) { 
                distinctPln.add(str.substring(i,k+1));
            }
            if ( (new Character(str.charAt(j))).equals(new Character(str.charAt(k)))) { 
                distinctPln.add(str.substring(j,k+1));
            } else {
                continue;
            }
        }
    }

    Iterator<String> distinctPlnItr = distinctPln.iterator();
    while ( distinctPlnItr.hasNext()) {
        System.out.print(distinctPlnItr.next()+ ",");
    }
    return distinctPln.size();

}
0
user5999909