Leetcode:最长回文子串
动态规划
动态规划需要具有最优子结构性质,子解之间存在交叠,而后面的解和前面有关系时使用。一般bottom-up 填表方式记录解,一般伴随着回溯的方法找到最优解。
一、Leetcode:最长回文子串
二、使用步骤
1.思路
为了找到最长的回文子串,我的第一个想法是通过找到第一个回文子串之后,向两边扩展,如果仍然是回文子串,继续扩展,否则通过目前的回文子串长度构成的窗,向下一位移动,观察后面的是否是目前这个大小的回文子串,如果是,继续扩展,重复以上步骤。但是该方法的时间复杂度很高,而且比较暴力;于是产生第二个方法思路:上一个方法的移动方式是从一边到另一边,通过滑窗方式寻找,而对于最大回文子串,应当从大向小的查找,也就是采用双指针,分别指向头尾,根据目前指针指向的字符是否相等而选择向内移动指针查看内部是否是回文子串,重复以上步骤。该方法很清晰而且易于实现。最后是题解的方法,即,动态规划,通过填一个二维表,横坐标为当前指针,纵坐标为另一个指针,易知,单个字符必然是回文子串,所以这个表的对角线全是回文子串;两个字符的,如果相等也是回文子串,所以坐标相差一的如果相等便是回文子串,对于三个字符的,如果首尾相等,也是回文子串;对于三个以上的,如果内部是回文子串且首尾相等,必然也是回文子串。对于后三种情况可以统一为:如果首位相等,且2、3字符的必然是回文子串,对于3个字符以上的,如果内部是回文子串,那也一定是回文子串。于是动态规划的递推已经得到,但是表里填什么呢,根据横纵坐标的设置方式可以知道,如果是回文子串,只需要根据二坐标就可以求出长度,那么表里只需要填是或者不是即可。此外,在划分情况的时候,主要的问题就是字符串的长度,于是在循环时,可以通过字符长度来循环,该方法时间复杂度为O(n^2)。
2.代码及实现过程
代码如下:
#include <vector>
class Solution {
public:
string longestPalindrome(string s)
{
int L; //表长度
int len = s.size();
int l = 1; //记录长度
int anss = 0;
vector<vector<int>> dp(len, vector<int>(len));
if(len<2)
{
return s;
}
for(int i=0;i<len;i++)
{
dp[i][i] = true; //一个字符必然回文
}
for(L=2;L<=len;L++)
{
for(int i=0;i<len;i++)
{
int j = i + L - 1; //定义j
if(j >= len)
{
break; //溢出
}
if(s[i] != s[j])
{
dp[i][j] = false;
}
else
{
if(j - i<3)
{
dp[i][j] = true;
}
else
{
dp[i][j] = dp[i+1][j-1];
}
}
if(dp[i][j] && j - i + 1>l)
{
l = L;
anss = i;
}
}
}
return s.substr(anss, l);
}
};
更新最长的长度的方法即表内是true还是false且目前长度是否大于当前最大。
总结
动态规划的主要问题在于填表和寻找子结构,最优则子最优。寻找到递推的关系,如何填表是主要问题,如何思考问题的本质,将问题一步步肢解,也是动态规划乃至其他算法问题的主要思考的地方。