Find all substrings that are palindromes

后端 未结 9 596
伪装坚强ぢ
伪装坚强ぢ 2020-12-07 21:28

If the input is \'abba\' then the possible palindromes are a, b, b, a, bb, abba.
I understand that determining if string is palindrome is easy. It would be like:

相关标签:
9条回答
  • 2020-12-07 21:39
        // 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 讨论(0)
  • 2020-12-07 21:42

    Code is to find all distinct substrings which are palindrome. Here is the code I tried. It is working fine.

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

    }

    OUTPUT:

    Possible substrings: a b b a ab bb ba abb bba abba

    Total possible substrings are 10

    Total palindromic substrings are 4

    Possible palindromic substrings: [bb, a, b, abba]

    It took 1 milliseconds

    0 讨论(0)
  • 2020-12-07 21:44

    So, each distinct letter is already a palindrome - so you already have N + 1 palindromes, where N is the number of distinct letters (plus empty string). You can do that in single run - O(N).

    Now, for non-trivial palindromes, you can test each point of your string to be a center of potential palindrome - grow in both directions - something that Valentin Ruano suggested.
    This solution will take O(N^2) since each test is O(N) and number of possible "centers" is also O(N) - the center is either a letter or space between two letters, again as in Valentin's solution.

    Note, there is also O(N) solution to your problem, based on Manacher's algoritm (article describes "longest palindrome", but algorithm could be used to count all of them)

    0 讨论(0)
  • 2020-12-07 21:47
    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);
    }
    }
    

    Try out this. Its my own solution.

    0 讨论(0)
  • 2020-12-07 21:48

    This can be done in O(n), using Manacher's algorithm.

    The main idea is a combination of dynamic programming and (as others have said already) computing maximum length of palindrome with center in a given letter.


    What we really want to calculate is radius of the longest palindrome, not the length. The radius is simply length/2 or (length - 1)/2 (for odd-length palindromes).

    After computing palindrome radius pr at given position i we use already computed radiuses to find palindromes in range [i - pr ; i]. This lets us (because palindromes are, well, palindromes) skip further computation of radiuses for range [i ; i + pr].

    While we search in range [i - pr ; i], there are four basic cases for each position i - k (where k is in 1,2,... pr):

    • no palindrome (radius = 0) at i - k
      (this means radius = 0 at i + k, too)
    • inner palindrome, which means it fits in range
      (this means radius at i + k is the same as at i - k)
    • outer palindrome, which means it doesn't fit in range
      (this means radius at i + k is cut down to fit in range, i.e because i + k + radius > i + pr we reduce radius to pr - k)
    • sticky palindrome, which means i + k + radius = i + pr
      (in that case we need to search for potentially bigger radius at i + k)

    Full, detailed explanation would be rather long. What about some code samples? :)

    I've found C++ implementation of this algorithm by Polish teacher, mgr Jerzy Wałaszek.
    I've translated comments to english, added some other comments and simplified it a bit to be easier to catch the main part.
    Take a look here.


    Note: in case of problems understanding why this is O(n), try to look this way:
    after finding radius (let's call it r) at some position, we need to iterate over r elements back, but as a result we can skip computation for r elements forward. Therefore, total number of iterated elements stays the same.

    0 讨论(0)
  • 2020-12-07 21:49

    I suggest building up from a base case and expanding until you have all of the palindomes.

    There are two types of palindromes: even numbered and odd-numbered. I haven't figured out how to handle both in the same way so I'll break it up.

    1) Add all single letters

    2) With this list you have all of the starting points for your palindromes. Run each both of these for each index in the string (or 1 -> length-1 because you need at least 2 length):

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

    I'm not sure if this helps the Big-O for your runtime, but it should be much more efficient than trying each substring. Worst case would be a string of all the same letter which may be worse than the "find every substring" plan, but with most inputs it will cut out most substrings because you can stop looking at one once you realize it's not the center of a palindrome.

    0 讨论(0)
提交回复
热议问题