Java 带递归解的最短回文问题

Java 带递归解的最短回文问题,java,algorithm,palindrome,Java,Algorithm,Palindrome,调试以下问题(递归解决方案)并混淆for循环的逻辑含义。如果有人有任何见解,欢迎分享 给定一个字符串S,可以通过在其前面添加字符将其转换为回文。查找并返回通过执行此转换可以找到的最短回文 例如: 给出“AAACECAA”,返回“aaacecaaa” 给定“abcd”,返回“dcbabcd” 基于KMP的解决方案 public class Solution { public String shortestPalindrome(String s) { String p = n

调试以下问题(递归解决方案)并混淆for循环的逻辑含义。如果有人有任何见解,欢迎分享

给定一个字符串S,可以通过在其前面添加字符将其转换为回文。查找并返回通过执行此转换可以找到的最短回文

例如:

给出“AAACECAA”,返回“aaacecaaa”

给定“abcd”,返回“dcbabcd”

基于KMP的解决方案

public class Solution {
    public String shortestPalindrome(String s) {
        String p = new StringBuffer(s).reverse().toString();
        char pp[] = p.toCharArray();
        char ss[] = s.toCharArray();
        int m = ss.length;
        if (m == 0) return "";

        // trying to find the greatest overlap of pp[] and ss[]
        // using the buildLPS() method of KMP
        int lps[] = buildLPS(ss);
        int i=0;// points to pp[]
        int len = 0; //points to ss[]

        while(i<m) {
            if (pp[i] == ss[len]) {
                i++;
                len++;
                if (i == m)
                    break;
            } else {
                if (len == 0) {
                    i++;
                } else {
                    len = lps[len-1];
                }
            }
        }
        // after the loop, len is the overlap of the suffix of pp and prefix of ss
        return new String(pp) + s.substring(len, m);

    }

    int [] buildLPS(char ss[]) {
        int m = ss.length;
        int lps[] = new int[m];
        int len = 0;
        int i = 1;
        lps[0] = 0;
        while(i < m) {
            if (ss[i] == ss[len]) {
                len++;
                lps[i] = len;
                i++;
            } else {
                if (len == 0) {
                    i++;
                } else {
                    len = lps[len-1];
                }

            }
        }

        return lps;
    }
}
公共类解决方案{
公共字符串最短回文(字符串s){
字符串p=新的StringBuffer.reverse().toString();
char pp[]=p.toCharArray();
char ss[]=s.toCharArray();
int m=ss长度;
如果(m==0)返回“”;
//试图找到pp[]和ss[]的最大重叠
//使用KMP的buildLPS()方法
int lps[]=buildLPS(ss);
int i=0;//指向pp[]
int len=0;//指向ss[]

虽然(我我最初的评论是不正确的-正如你所指出的,除了使用
j
”检查
s
是否是完整的回文外,
j
还用于查找(智能猜测?)环绕该索引进行换行+反转字符串开头可能存在的最长回文中的尾随字符。我对该算法的理解如下:

e、 g.
aacecaa
给出
j=7
,导致

`aacecaaa` is `aacecaa` (palindrome) + `a` (suffix)
因此,最短的回文结尾是:

`a` (suffix) + `aacecaa` + `a` (suffix)
如果后缀由多个字符组成,则必须将其反转:

`aacecaaab` is `aacecaa` (palindrome) + `ab` (suffix)
因此,这种情况下的解决方案是:

`ba` + `aacecaa` + `ab` (suffix)
在最坏的情况下,
j=1
(因为
a
将在
i=0
j=0
时匹配),例如,
abcd
中没有回文序列,所以最好的办法是将第一个字符环绕起来

dcb
+
a
+
bcd

老实说,我不是100%相信您正在调试的算法在所有情况下都能正常工作,但似乎找不到失败的测试用例。该算法肯定不是直观的

编辑

我相信最短回文可以确定地导出,根本不需要递归-似乎在您正在调试的算法中,递归掩盖了j值的副作用。在我看来,以下是一种以更直观的方式确定
j
的方法:

private static String shortestPalindrome(String s) {
    int j = s.length();
    while (!isPalindrome(s.substring(0, j))) {
        j--;
    }
     String suffix = s.substring(j);
    // Similar to OP's original code, excluding the recursion.
    return new StringBuilder(suffix).reverse()
              .append(s.substring(0, j))
              .append(suffix)
              .toString();  
}

我在

上粘贴了一些带有
isAlindrome
实现的测试用例,我最初的评论是不正确的——正如您所指出的,除了使用
j
'检查
s
是否是完整的回文外,
j
还用于查找(智能猜测?)环绕该索引进行换行+反转字符串开头可能存在的最长回文中的尾随字符。我对该算法的理解如下:

e、 g.
aacecaa
给出
j=7
,导致

`aacecaaa` is `aacecaa` (palindrome) + `a` (suffix)
因此,最短的回文结尾是:

`a` (suffix) + `aacecaa` + `a` (suffix)
如果后缀由多个字符组成,则必须将其反转:

`aacecaaab` is `aacecaa` (palindrome) + `ab` (suffix)
因此,这种情况下的解决方案是:

`ba` + `aacecaa` + `ab` (suffix)
在最坏的情况下,
j=1
(因为
a
将在
i=0
j=0
时匹配),例如,
abcd
中没有回文序列,所以最好的办法是将第一个字符环绕起来

dcb
+
a
+
bcd

老实说,我不是100%相信您正在调试的算法在所有情况下都能正常工作,但似乎找不到失败的测试用例。该算法肯定不是直观的

编辑

我相信最短回文可以确定地导出,根本不需要递归-似乎在您正在调试的算法中,递归掩盖了j值的副作用。在我看来,以下是一种以更直观的方式确定
j
的方法:

private static String shortestPalindrome(String s) {
    int j = s.length();
    while (!isPalindrome(s.substring(0, j))) {
        j--;
    }
     String suffix = s.substring(j);
    // Similar to OP's original code, excluding the recursion.
    return new StringBuilder(suffix).reverse()
              .append(s.substring(0, j))
              .append(suffix)
              .toString();  
}

我已经用
isPalindrome
的一个实现粘贴了一些测试用例,谢谢@StuartLC,当字符串本身是回文时,理解j的逻辑含义,我的问题更多的是当字符串本身不是回文时,j的逻辑含义是什么。似乎j总是在进一步的递归中使用,无论是哪种类型条件。感谢您的任何见解。:)由于代码段缺少注释,因此它是错误的。循环假定从字符串末尾开始以相同顺序排列遇到的每个字符都在合适的中间索引-猜测之前。(请尝试一下
acea
acacia
)@greybeard,我发布了一个基于KMP的解决方案,你怎么看?更干净,逻辑正确了?谢谢。:)谢谢@StuartLC,当字符串本身是回文时,理解j的逻辑含义,我的问题更多的是当字符串本身不是回文时,j的逻辑含义是什么。似乎j总是用在进一步的r中无论哪种情况,都是ecursive。任何见解都值得赞赏。:)由于代码段缺少注释,因此它是错误的。循环假定从字符串末尾开始以相同顺序排列遇到的每个字符都在合适的中间索引-猜测之前。(请尝试一下
acea
acacacacia
)@greybeard,我发布了一个基于KMP的解决方案,你怎么看?现在更干净,逻辑正确了?谢谢。:)在StringBuilder上使用toString而不是它的附加功能有什么特别的原因吗?@Clashsoft很好,我们应该使用
StringBuilder
,而不是
StringBuffer
@StuartLC,很好的捕捉和欣赏所有的评论。我发布了一个基于KMP的解决方案,你怎么看?更干净,更符合逻辑