给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
动态规划
代码:
//动态规划
public static String longestPalindrome(String s) {
final int length = s.length();
int maxLength = 0;
int start = 0;
boolean[][] P = new boolean [length][length];
if (s.length() == 0){
return "";
}
for (int i = 0; i < length; i++){
P[i][i] = true;
if (i < length - 1 && s.charAt(i) == s.charAt(i + 1)){
P[i][i+1] = true;
start = i;
maxLength = 2;
}
}
for (int len = 3 ; len <= length ;len ++){ //字串长度
for (int i = 0 ; i <= length - len; i++){
/**
* 子串结束地址
*/
int j=i+len-1;
if(P[i+1][j-1]&&s.charAt(i)==s.charAt(j))
{
P[i][j]=true;
maxLength=len;
start=i;
}
}
}
if (start == 0 && maxLength == 0){
return s.charAt(s.length() - 1)+"";
}else if(maxLength >= 2) {
return s.substring(start, start + maxLength);
}
return null;
}
分析:
假设s = "abbba"
长度为1的子串本身就是回文字符串:
a | b | b | b | a | |
a | true | ||||
b | true | ||||
b | true | ||||
b | true | ||||
a | true |
长度为2的子串有:ab bb bb ba
a | b | b | b | a | |
a | true | false | |||
b | true | true | |||
b | true | true | |||
b | true | false | |||
a | true |
那么长度为3的子串中,是回文字符串的充要条件就是首位字符相等,中间字符为回文字符串。
那么状态转移方程为:P[i+1][j-1]&&s.charAt(i)==s.charAt(j)
for (int i = 0 ; i <= length - 3; i++){
/**
* 子串结束地址
*/
int j=i+len-1;
if(P[i+1][j-1]&&s.charAt(i)==s.charAt(j))
{
P[i][j]=true;
maxLength=len;
start=i;
}
}
长度为3的子串有:abb、bbb、bba
S【0,2】是否是回文字符串需要判断S【1,1】
S【1,3】是否是回文字符串需要判断S【2,2】
S【2,4】是否是回文字符串需要判断S【3,3】
a | b | b | b | a | |
a | true | false | false | ||
b | true | true | true | ||
b | true | true | false | ||
b | true | false | |||
a | true |
长度为4的子串有:abbb、bbba
S【0,3】是否是回文字符串需要判断S【1,2】
S【1,4】是否是回文字符串需要判断S【2,3】
a | b | b | b | a | |
a | true | false | false | false | |
b | true | true | true | false | |
b | true | true | false | ||
b | true | false | |||
a | true |
长度为5的子串有:abbba
S【0,4】是否是回文字符串需要判断S【1,3】
a | b | b | b | a | |
a | true | false | false | false | true |
b | true | true | true | false | |
b | true | true | false | ||
b | true | false | |||
a | true |
动态规划问题求解思路
1. 将原问题分解为子问题
把原问题分解为若干个子问题,子问题和原问题形式相同或类似,只不过规模变小了。子问题都解决,原问题即解决(数字三角形例)。
子问题的解一旦求出就会被保存,所以每个子问题只需求 解一次。
2. 定状态
在用动态规划解题时,我们往往将和子问题相关的各个变量的一组取值,称之为一个“状 态”。一个“状态”对应于一个或多个子问题, 所谓某个“状态”下的“值”,就是这个“状 态”所对应的子问题的解。
所有“状态”的集合,构成问题的“状态空间”。“状态空间”的大小,与用动态规划解决问题的时间复杂度直接相关。 在本例中一共有(1+N)*N/2个子串,所以这个问题的状态空间里一共就有(1+N)*N/2个状态。
整个问题的时间复杂度是状态数目乘以计算每个状态所需时间。
3. 确定一些初始状态(边界状态)的值
以“求最长回文字串”为例,初始状态就是字串长度为1,然后长度为2 ……。
4. 确定状态转移方程
定义出什么是“状态”,以及在该“状态”下的“值”后,就要找出不同的状态之间如何迁移――即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”(递推型)。状态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程”。