Java 查找所有回文的子字符串

Java 查找所有回文的子字符串,java,algorithm,substring,palindrome,Java,Algorithm,Substring,Palindrome,如果输入是abba,那么可能的回文是a,b,b,a,bb,abba。 我知道确定字符串是否是回文是很容易的。就像: 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 tr

如果输入是abba,那么可能的回文是a,b,b,a,bb,abba。 我知道确定字符串是否是回文是很容易的。就像:

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

但是找到回文子字符串的有效方法是什么呢?

也许你可以迭代潜在的中字符奇数长度回文和字符偶数长度回文之间的中点,并扩展每一个,直到你不能再得到下一个左右字符不匹配为止

当字符串中没有很多回文时,这将节省大量计算。在这种情况下,成本将取决于稀疏的回文字符串

对于回文密集输入,它将位于^2上,因为每个位置的长度不能超过数组的长度/2。显然,在阵列的末端,这甚至更少

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

我建议从一个基本案例开始构建并扩展,直到你拥有所有的palindomes

回文有两种类型:偶数和奇数。我还没有弄明白如何用同样的方式处理这两个问题,所以我要把它分开

1添加所有单个字母

有了这个列表,你就有了回文的所有起点。为字符串中的每个索引或1->length-1运行这两个命令,因为您至少需要2个长度:

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

我不确定这是否有助于运行时使用Big-O,但它应该比尝试每个子字符串更有效率。最坏的情况是一个由所有相同字母组成的字符串,这可能比“查找每个子字符串”计划更糟糕,但对于大多数输入,它将删除大多数子字符串,因为一旦你意识到它不是回文的中心,你就可以不再看它了。

因此,每个不同的字母都已经是回文-因此你已经有了N+1个回文,其中N是不同字母加上空字符串的数量。你可以在单次跑步中做到这一点

现在,对于非平凡回文,您可以测试字符串的每个点,使其成为潜在回文的中心—双向增长—这是Valentin Ruano建议的。 此解决方案将采用^2,因为每个测试都在进行中,并且可能的中心数也在进行中-中心要么是一个字母,要么是两个字母之间的空格,就像Valentin的解决方案一样

请注意,还有一篇关于解决您的问题的“基于算法”的文章介绍了最长回文,但该算法可用于计算所有回文的数量

这可以在ON中完成,使用。 其主要思想是将动态规划和其他人所说的结合起来,计算给定字母中带中心的回文的最大长度

我们真正想计算的是最长回文的半径,而不是长度。 对于奇数长度回文,半径仅为length/2或length-1/2

在计算给定位置i的回文半径pr之后,我们使用已经计算的半径来查找范围[i-pr;i]内的回文。这让我们能够,因为回文是,嗯,回文跳过了范围[i;i+pr]半径的进一步计算

当我们在[i-pr;i]范围内搜索时,每个位置i-k有四种基本情况,其中k位于1,2,。。。公共关系:

i-k处无回文半径=0 这意味着i+k处的半径也为0 内部回文,这意味着它符合范围 这意味着i+k处的半径与i-k处的半径相同 外部回文,这意味着它不符合范围 这意味着在i+k处的半径被减小以适应范围,即因为i+k+radius>i+pr,我们将半径减小为pr-k 粘性回文,意思是i+k+radius=i+pr 在这种情况下,我们需要在i+k处搜索可能更大的半径 完整、详细的解释将相当长。一些代码示例怎么样

我找到波兰老师,MGR JeZi-WaasasZek的C++算法实现。 我已经将评论翻译成英语,添加了一些其他的评论,并将其简化了一点,以便更容易抓住主要部分。

注意:如果在理解为什么打开时遇到问题,请尝试这样看:
在找到半径后,我们在某个位置将其称为r,我们需要对r元素进行迭代,但结果是我们可以跳过对r元素的计算。因此,迭代元素的总数保持不变

我刚刚提出了自己的逻辑,这有助于解决这个问题。 快乐编码..:-

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

我尝试了下面的代码,它在这些情况下运行良好 它还处理单个字符

通过的案例很少:

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]
代码


希望它的好代码是找到所有不同的回文子串。 这是我试过的代码。它工作得很好

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

输出:

可能的子字符串: A. B B A. ab bb 文学士 神甫 工商管理学士 阿巴

可能的子字符串总数为10

回文子串总数为4

可能的回文子串:[bb,a,b,abba]

花了1毫秒

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

试试这个。这是我自己的解决方案。

以您的例子,您希望
也获取bab和baab?我本以为不会,因为bab和baab不是字符串的一部分,除非您先更改字符的顺序。这不是一种有效的方法,但您可以获取每个子字符串,并检查它是否是回文。它只需要^3次可能的回文是a,b,b,a,bb,abba,所以我们可以根据它们在原始字符串中的位置计算其中一些回文两次?这看起来可以大大简化问题。也许您可以迭代潜在的中间字符奇数长度回文和字符偶数长度回文之间的中点,并对每个回文进行扩展,直到无法再获得下一个不匹配的左右字符。当字符串中没有很多回文时,这将节省大量计算。在这种情况下,成本将取决于稀疏的回文字符串。对于回文,稠密将位于^2上,因为每个位置的长度不能超过数组的长度/2。很明显,这在数组的末尾就更少了。很好。请注意,在OP的原始示例中,这将只返回一次a和b。如果有必要跟踪每个事件,那么很容易定义一个同时包含字符串和起始位置的类,然后将结果设置为该类的集合,而不是集合。如果映射返回的键是字符串和值(例如,第一个找到的起始点),则会做得很好。s[i]==s[j]此语句是错误的。。你是如何从基于索引的字符串中提取字符的?@Rahussahu感谢你的观察,我从未编译过代码,因此可能会有这些小问题。你只需在每两个相邻字母之间插入一些特殊字符,就可以将两个可能的大小写减少到只有奇数长度的回文,例如abba->abba。如果使用两个端点索引,而不是中心索引和半径i,则可以以相同的方式处理这两种情况。查看我在上面发布的解决方案。+1谢谢。很明显,填充半径数组的动态算法是开着的,但是我不确定迭代这个数组表示的所有可能的回文也是开着的。你是说打印结果的部分吗?这是不可能的,因为在字符串2上的字符串打印出来的结果-考虑字符串AAAAAAAAAA,结果。然而,数组本身大小不同,包含打印回文所需的所有数据。要选择哪种结果表示形式?仅在上打印数组还是在^2上打印所有回文?完全独立于算法本身。谢谢您的详细回答。它确实帮助我理解了它。我个人认为这并不能回答OP的问题——因为这是最长回文子串的算法,而不是列出所有回文子串。@Nhan请更仔细地分析算法。它首先在给定位置找到最长的回文子串,然后使用它在最长的回文子串中找到回文。然后在下一个尚未分析的位置搜索最长的回文子串。。。等等试着把步骤画在纸上,它就会变得非常清晰。
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);
    }
}
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;
}
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);
}
}