leetcode 5:最长回文子串:给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。(难度:中等)
回文:把相同的词汇或句子 ,在下文中调换位置或颠倒过来,产生首尾回环的情趣,叫做回文,也叫回环 。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
1、暴力法
最容易想到的就是暴力破解,求出每一个子串,之后判断是不是回文,找到最长的那个。
求每一个子串时间复杂度O(N^2),判断子串是不是回文O(N),两者是相乘关系,所以时间复杂度为O(N^3)。
class Solution {
public:
string longestPalindrome(string s) {
int length = s.size();
int maxlen = 0;
int start = 0;
for(int i = 0; i<length; ++i){
for(int j=i+1; j<length; ++j){
int m = 0, n = 0;
for(m = i, n = j; m < n; ++i, --j){
if(s.at(m) != s.at(n))
break;
}
if(m >= n && j-i > maxlen){
maxlen = j=i-1;
start = i;
}
}
}
if(maxlen > 0)
return s.substr(start, maxlen);
return NULL;
}
};
2、动态规划
动态规划问题是面试题中的热门话题,如果要求一个问题的最优解(通常是最大值或者最小值),而且该问题能够分解成若干个子问题,并且小问题之间也存在重叠的子问题,则考虑采用动态规划。
使用动态规划特征:
1. 求一个问题的最优解
2. 大问题可以分解为子问题,子问题还有重叠的更小的子问题
3. 整体问题最优解取决于子问题的最优解(状态转移方程)
4. 从上往下分析问题,从下往上解决问题
5. 讨论底层的边界问题
回文字符串的子串也是回文,比如P[i,j](表示以i开始以j结束的子串)是回文字符串,那么P[i+1,j-1]也是回文字符串。这样最长回文子串就能分解成一系列子问题了。这样需要额外的空间O(N^2),算法复杂度也是O(N^2)。
首先定义状态方程和转移方程:
string longestPalindrome(string s)
{
const int n = s.size();
bool dp[n][n];
memset(dp, 0, sizeof(dp));
int maxlen = 1; //保存最长回文子串长度
int start = 0; //保存最长回文子串起点
for(int i = 0; i < n; ++i)
{
for(int j = 0; j <= i; ++j)
{
if(i - j < 2)
{
dp[j][i] = (s[i] == s[j]);
}
else
{
dp[j][i] = (s[i] == s[j] && dp[j + 1][i - 1]);
}
if(dp[j][i] && maxlen < i - j + 1)
{
maxlen = i - j + 1;
start = j;
}
}
}
return s.substr(start, maxlen);
}