[LeetCode] 5. Longest Palindromic Substring 最长回文子串
本题求给定字符串的最长回文子串,首先可以想到使用暴力的方法,求出给定字符串的所有的回文子串的长度,取长度最长的子串,具体地分回文子串长度为奇数和长度为偶数讨论,时间复杂度O(n^2),但此暴力求解的方法在leetcode上会报超时错误,具体代码如下:
一. 暴力法
参考代码
#include<string> #include<string.h> using namespace std; class Solution { public: string longestPalindrome(string s) { if(s.empty()||s.size()<=1) return s; int size = s.size(); string odd_str = longestPalindrome_odd(s,size); string even_str = longestPalindrome_even(s,size); return odd_str.size() >= even_str.size()?odd_str:even_str; } string longestPalindrome_odd(string& s,int size) { string res; for(int i = 0; i<size; ++i){ string substr = s.substr(i,1); for(int l = i-1,r = i+1;l>=0 && r<=size-1;l--,r++){ if(s[l]!=s[r]) break; else{ substr = s.substr(l,r-l+1); } } if(substr.size()>=res.size()) res = substr; } return res; } string longestPalindrome_even(string& s,int size) { string res; for(int i = 0; i<size; ++i){ string substr; for(int l = i,r = i+1;l>=0&&r<=size-1;l--,r++){ if(s[l]!=s[r]) break; else{ substr = s.substr(l,r-l+1); } } if(substr.size()>=res.size()) res = substr; } return res; } };
二 动态规划:
第 1 步:定义状态
dp[i][j] 表示子串 s[i, j] 是否为回文子串。定义状态先尝试“题目问什么,就把什么设置为状态”。然后考虑“状态如何转移”,如果“状态转移方程”不容易得到,尝试修改状态定义,目的仍然是为了方便得到“状态转移方程”。
第 2 步:思考状态转移方程
这一步在做分类讨论(根据头尾字符是否相等),根据对状态定义的分析得到:
dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]
分析这个状态转移方程:
(1)“动态规划”事实上是在填一张二维表格,i 和 j 的关系是 i <= j ,因此,只需要填这张表的上半部分;
(2)在 s[i] == s[j] 成立和 j - i < 3 的前提下,直接可以下结论,dp[i][j] = true,否则才执行状态转移。
注:分类讨论是构造转态转移方程的技巧。对状态空间进行分类,思考最优子结构到底是什么。即大问题的最优解如何由小问题的最优解得到。
第 3 步:考虑初始化
初始化的时候,单个字符一定是回文串,因此把对角线先初始化为 1,即 dp[i][i] = 1 。
事实上,初始化的部分都可以省去。因为只有一个字符的时候一定是回文,dp[i][i] 根本不会被其它状态值所参考。
第 4 步:考虑输出
只要一得到 dp[i][j] = true,就记录子串的长度和起始位置,没有必要截取,因为截取字符串也要消耗性能,记录此时的回文子串的“起始位置”和“回文长度”即可。
第 5 步:考虑状态是否可以压缩
因为在填表的过程中,只参考了左下方的数值。事实上可以压缩,但会增加一些判断语句,增加代码编写和理解的难度,丢失可读性。在这里不做状态压缩。
下面是编码的时候要注意的事项:总是先得到小子串的回文判定,然后大子串才能参考小子串的判断结果。
思路是:
1、在子串右边界 j 逐渐扩大的过程中,枚举左边界可能出现的位置;
2、左边界枚举的时候可以从小到大,也可以从大到小。
参考代码2 :
class Solution { public: string longestPalindrome(string s) { const int size = s.size(); if(s.empty()||size <= 1) return s; int l=0,h=0,seq=0; bool dp[size][size]; for(int j = 1;j<size;++j) for(int i = 0;i<=j;++i) { int sub_seq = j-i+1; if(sub_seq<3) { dp[i][j]=s[i]==s[j]; } else { dp[i][j]=(s[i]==s[j]&&dp[i+1][j-1]); } if(dp[i][j]&&sub_seq>=seq){ l=i; h=j; seq=sub_seq; } } return s.substr(l,seq); } };