找到所有作为回文的子串

时间:2013-11-05 23:22:18

标签: 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 true;  
}

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

9 个答案:

答案 0 :(得分:29)

可以使用Manacher's algorithmO(n)中完成此操作。

主要思想是动态编程和(正如其他人已经说过的)计算最大长度的回文以及给定字母中心的组合。


我们真正想要计算的是最长回文的 radius ,而不是长度。 radius 只是length/2(length - 1)/2(对于奇长的回文)。

在给定位置 pr 计算回文半径 i 后,我们使用已计算的半径来查找回文范围[ i - pr ; i ]。这让我们(因为回文是好的,回文)会跳过范围radiuses [ i ; i + pr ]的进一步计算。

我们搜索范围[ i - pr ; i ]时,每个职位有四种基本情况 i - k (其中 k 位于 1,2,... pr ):

    radius = 0
  • 无回文( i - k ) (这也意味着 radius = 0 i + k
  • 内部回文,这意味着它适合范围
    (这意味着 radius i + k i - k 相同
  • 外部回文,这意味着它不适合范围
    (这意味着 radius i + k 减少以适应范围,即因为 {{ 1}} 我们将 i + k + radius > i + pr 缩减为 radius
  • 粘性回文,即 pr - k
    (在这种情况下,我们需要在 i + k + radius = i + pr 搜索可能更大的半径

完整,详细的解释会很长。一些代码示例怎么样? :)

我找到了波兰老师,JerrWałaszek的这个算法的C ++实现 我已将评论翻译成英文,添加了一些其他评论并简化了一些以便更容易理解主要部分 Take a look here


注意:如果有问题需要了解原因i + k,请尝试这样: 在某个位置找到 radius (我们称之为 O(n) )之后,我们需要重新遍历 r 元素,但结果我们可以跳过 r 元素的计算。因此,迭代元素的总数保持不变。

答案 1 :(得分:18)

也许你可以迭代潜在的中间字符(奇数长度的回文)和字符之间的中间点(甚至是长度的回文)并延伸每个字符直到你无法进一步(下一个左右字符不匹配)。

当字符串中没有多个palidromes时,这将节省大量的计算。在这种情况下,稀疏的palidrome串的成本为O(n)。

对于回文密集输入,它将是O(n ^ 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++;
      }
  }

答案 2 :(得分:9)

所以,每个不同的字母已经是一个回文 - 所以你已经有N + 1个回文,其中N是不同字母的数量(加上空字符串)。你可以在单次运行中做到 - O(N)。

现在,对于非平凡的回文,你可以测试你的弦的每个点是潜在回文的中心 - 在两个方向上生长 - Valentin Ruano 建议。
该解决方案将采用O(N ^ 2),因为每个测试是O(N)和可能的数量&#34;中心&#34;也是O(N) - center是两个字母之间的字母或空格,同样在Valentin的解决方案中。

注意,基于Manacher's算法,你的问题也有O(N)解决方案(文章描述&#34;最长的回文&#34;,但算法可以用来统计所有这些)< / p>

答案 3 :(得分:6)

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

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

答案 4 :(得分:1)

我建议从基础案例中建立并扩展,直到你拥有所有的palindomes。

有两种类型的回文:偶数和奇数。我还没弄明白如何以同样的方式处理两者,所以我会分手。

1)添加所有单个字母

2)使用此列表,您可以获得所有回文的起点。为字符串中的每个索引运行这两个(或1 - &gt; 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,但它应该比尝试每个子字符串更有效。最糟糕的情况是所有相同字母的字符串可能比“查找每个子字符串”计划更糟糕,但是对于大多数输入,它会删除大多数子字符串,因为一旦您意识到它不是一个中心,您就可以停止查看一个子字符串。回文。

答案 5 :(得分:0)

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

}

答案 6 :(得分:0)

我尝试了以下代码,并且它适用于案例 它也处理单个字符

几乎没有通过的案件:

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]

代码

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

希望它很好

答案 7 :(得分:0)

代码是查找回文的所有不同子串。 这是我试过的代码。它工作正常。

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

}

输出:

可能的子串: 一个 b b 一个 AB BB BA ABB BBA ABBA

总可能的子串是10

总回文子串为4

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

花了1毫秒

答案 8 :(得分:0)

    function npa($id)
    {
       $news = Post::where('category_id', $id)->orderBy('created_at', 'desc')->paginate(12);
       return view('npa', compact('news'));
    }

尝试一下。这是我自己的解决方案。