要明确的一点是:一个长度大于等于3的回文串,去掉首尾两个字母以后,剩下的也是一个回文串。所以我们可以用 dp[ i ][ j ] = true OR false 来表示字符串第 i 个字符到第 j 个字符组成的串是否是回文串。
所以状态转移方程就是:
dp[ i ][ j ] = dp[ i + 1 ][ j - 1 ] && s[ i ] == s[ j ] (dp[ i + 1 ][ j - 1 ] 就是 dp[ i ][ j ] 去掉首尾字母以后的串)
以上的方程都是基于 i 到 j 组成的串长度是大于2的。那么串长度等于1或者2是怎么样的呢?
①当i 到 j 组成的串长度为 2 时,只要 s[ i ] == s[ j ],那么就是回文串,像:aa、bb、cc等。
②当i 到 j 组成的串长度为 1 时,是回文串。
故需要先初始化长度为1、2的回文子串,再由此基础上推导出长度为3或以上的串是否为回文串。
整个算法的思路就是由低长度子串的回文性推导出高长度子串的回文性,长度为1、2的子串的回文性是需要先进行初始化的
public class Palindrome {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String subStr = palindrome(sc.next);
System.out.println(subStr);
}
// 传入字符串,返回最长子串
String palindrome(String str) {
if (str.length() == 0) {
return "";
}
int len = str.length();
int startIndex = 0; // 最长子串开始下标
int maxLen = 0; // 最长子串的长度,可以结合开始下标 求子串结束下标的
boolean[][] dp = new boolean[len][len];
for (int strLen = 0; strLen < len; strLen++) { // 从长度为1的子串开始求起
for (int i = 0; i + strLen < len; i++) { // 遍历子串的开始下标
int j = i + strLen; // 子串的结束下标。则此时表示i 到 j 的长度为 strlen + 1的子串
if (strLen == 0) { // 长度为1的子串,都是回文串
dp[i][j] = true;
} else if (strLen == 1) { // 长度为2的子串,只要两个字母相等,就是回文串
dp[i][j] = (str.charAt(i) == str.charAt(j));
} else {
dp[i][j] = (dp[i + 1][j - 1] && str.charAt(i) == str.charAt(j));
}
if (dp[i][j]) {
maxLen = strLen;
startIndex = i;
}
}
}
return str.subString(startIndex, startIndex + maxLen + 1);
}
外层循环是遍历所有的长度子串,所以子串长度 strLen 是从0 遍历到 len - 1。
内层循环是遍历确定长度子串的开始下标。所以开始下标 i 从 0 遍历到 i + strLen < len。
结束下标就是 j = i + strLen