1. 题目
2. 分析
官网有DP的分析,但是没有直接给出代码实现。经过之前的背包9讲的学习,可以很快写出状态定义:
表示字符串从
到
的最长子串,然后状态转移方程为
该状态转移方程的初始条件可能有两种情况: 。 到这里只是DP分析的第一步,因为我们明显写出来了状态转移方程,所以其满足最优子结构。然后由状态转移方程可以发现其满足无后效性。
然后写伪代码:
for i=0 to s.size()-1
F[i][i]=1
//这里也可以当成DP小问题去做
for i=1 to s.size()-1
if s[i-1] == s[i]
F[i-1][i]=F[i-1][i-1]
update max
//这里很关键,错误的代码:
for i=0 to s.size()-2
for j=1 to s.size()-1
if(i != j&&s[i]==s[j])
F[i][j]=F[i+1][j-1]
update max
如果从背包入门DP,一定会在这里跌倒。上面的for
的上下限,将使状态转移方程不满足无后效性。可以很明显看出来F[i][j]=F[i+1][j-1]
在当前的情况使未来和过去产生了关系。
由无后效性出发,观察状态转移方程画出下图:
必须让i,j
的变化满足无后效性。所以采用下面的策略:
for len=3 to s.size()
for i=0 to s.size()-(len-1)
j=i+(len-1)
if s[i] ==s[j]
F[i][j] = F[i+1][j-1]
update max
3. AC代码
按照上面的分析,可以很快写出来:
class Solution {
public:
string longestPalindrome(string s) {
vector<vector<int>> F(s.size());
int left=0,tmp=0,max=0;
if(s.size() == 1)
return s;
for(int i=0;i<s.size();i++)
F[i].resize(s.size());
for(int i=0;i<s.size();i++)
F[i][i]=1;
for(int i=0;i+1 < s.size();i++){
if( s[i] == s[i+1]){
F[i][i+1] = F[i][i];
tmp=2;
}
else
tmp=1;
if(max < tmp){
left=i;
max=tmp;
}
}
for(int len=3;len <= s.size(); len++){
for( int i=0,j; i< s.size()-(len-1); i++){
j=len-1+i;
if( s[i] == s[j])
F[i][j] = F[i+1][j-1];
if(F[i][j] == 1){
if(max < len){
left=i;
max=len;
}
}
}
}
return s.substr(left,max);
}
};
4. 总结
笔者是记录到《DP学习系列》从零开始学习动态规划,混合背包(四)时,重新做了这个题,收获颇丰。如果单纯是看背包九讲一定会犯上面的错误,但是正是因为上面的错误,可以更加认识DP,重审最优子结构和无后效性的概念(可以重新思考背包9讲for
的上下限为什么符合)。最后笔者现在对以前提出的问题有了答案(通过debug状态转移数组等等),DP的解的数量取决于初始条件中符合最优子结构的数量,但是对于每一个初始条件解是唯一的。