法一(动态规划)
/**
* 法一(动态规划)
* 时间复杂度:O(n^2)
* 1. 确定dp数组以及下标的含义
* dp[i][j]表示从s[i]到s[j]之间的子串是否是回文子串,若是,dp[i][j] = true,否则,dp[i][j] = false
* 2. 确定递推公式
* (1)s[i] == s[j]
* 若s[i+1]到s[j-1]是回文子串,则s[i]到s[j]是回文子串;
* 若s[i+1]到s[j-1]不是回文子串,则s[i]到s[j]也不是回文子串
* (2)s[i] != s[j]
* s[i]到s[j]不是回文子串
* (3)由此可得递推公式
* s[i] == s[j] dp[i][j] = dp[i+1][j-1]
* s[i] != s[j] dp[i][j] = false
* 3. 确定dp数组如何初始化
* (1)dp[i][i] = true
* (2)s[i] == s[i+1] dp[i][i+1] = true
* (3)s[i] != s[i+1] dp[i][i+1] = false
* 4. 确定遍历顺序
* (1)按子串的长度和子串的初始位置进行枚举
* (2)第一遍将长度为3的子串dp值求出
* (3)第二遍根据第一遍的计算结果计算出长度为4的子串dp值,以此类推...
*
* @param s
* @return
*/
public String longestPalindrome(String s) {
int lens = s.length(), maxLen = 1;
boolean[][] dp = new boolean[lens][lens]; // 数组元素是boolean型默认初始化值为false
int left = 0, right = 0;
for (int i = 0; i < lens; i++) {
dp[i][i] = true;
if (i < lens - 1 && s.charAt(i) == s.charAt(i + 1)) {
dp[i][i + 1] = true;
maxLen = 2;
left = i;
right = i + 1;
}
}
for (int L = 3; L <= lens; L++) {
for (int i = 0; i + L - 1 < lens; i++) {
int j = i + L - 1;
if (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]) {
dp[i][j] = true;
maxLen = L;
left = i;
right = j;
}
}
}
return s.substring(left, right + 1);
}
法二(动态规划)
/**
* 法二(动态规划)
* 时间复杂度:O(n^2)
* 1. 确定dp数组以及下标的含义
* dp[i][j]表示区间范围[i,j]的子串是否是回文子串,若是,dp[i][j] = true,否则,dp[i][j] = false
* 2. 确定递推公式
* (1)s[i] == s[j]
* 若下标i与j相同,是回文子串
* 若下标i与j相差为1,是回文子串
* 若下标i与j相差大于1,则看dp[i + 1][j - 1]是否为true,若为true,则是回文子串,否则不是回文子串
* (2)s[i] != s[j]
* s[i]到s[j]不是回文子串
* (3)由此可得递推公式
* s[i] == s[j] j - i <= 1 ? dp[i][j] = true : dp[i][j] = dp[i+1][j-1]
* s[i] != s[j] dp[i][j] = false
* 3. 确定dp数组如何初始化
* dp[i][j] = false
* 4.确定遍历顺序
* dp[i + 1][j - 1]在dp[i][j]的左下角
* 所以一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的
*
* @param s
* @return
*/
public String longestPalindrome_2(String s) {
int lens = s.length(), maxLen = 1;
boolean[][] dp = new boolean[lens][lens]; // 数组元素是boolean型默认初始化值为false
int left = 0, right = 0;
for (int i = lens - 1; i >= 0; i--) {
for (int j = i; j < lens; j++) {
if (s.charAt(i) == s.charAt(j) && (j - i <= 1 || dp[i + 1][j - 1])) {
dp[i][j] = true;
}
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
left = i;
right = j;
}
}
}
return s.substring(left, right + 1);
}
法三(双指针)
/**
* 法三(双指针)
* 时间复杂度:接近O(n)
*
* @param s
* @return
*/
public String longestPalindrome_3(String s) {
if (s == null || s.length() == 0) {
return "";
}
int[] range = new int[2]; // 保存起始位置
char[] str = s.toCharArray();
for (int i = 0; i < s.length(); i++) { // 把回文看成中间的部分全是同一字符,左右部分相对称
i = findLongest(str, i, range); // 找到下一个与当前字符不同的字符
}
return s.substring(range[0], range[1] + 1);
}
/**
* 根据当前字符向两边扩散,返回下一个与当前字符不同的字符
*
* @param str
* @param left
* @param range
* @return
*/
private static int findLongest(char[] str, int left, int[] range) {
int right = left; // left为中间部分的第一个字符
while (right < str.length - 1 && str[right + 1] == str[left]) { // 查找中间部分
right++;
}
int ans = right; // right为中间部分的最后一个字符
while (left > 0 && right < str.length - 1 && str[left - 1] == str[right + 1]) { // 从中间向左右扩散
left--;
right++;
}
if (right - left > range[1] - range[0]) { // 记录最大长度
range[0] = left;
range[1] = right;
}
return ans;
}
本地测试
/**
* 5. 最长回文子串
*/
lay.showTitle(5);
Solution5 sol5 = new Solution5();
String s5 = "babad";
System.out.println(sol5.longestPalindrome(s5));
System.out.println(sol5.longestPalindrome_2(s5));
System.out.println(sol5.longestPalindrome_3(s5));