动态规划<1>最长回文子串(longest-palindromic-substring)
本节为动态规划第一题:最长回文字串(longest-palindromic-substring),主要包括:问题描述,算法思路,复杂度分析,C++实现。
1. 问题描述
给定一个字符串s,找到s中最长的回文子串,假设s的最大长度为1000,比如:给定一个字符串s,找到s中最长的回文子串,假设s的最大长度为1000,比如:
2. 解法
这里以动态规划为主,介绍三种解法的思路及其编程实现,分别为暴力法、动态规划法、中心扩展算法,下面是C++代码:
using namespace std;
class Solution_DP
{
public:
//最长回文子串的三种解法
//1. 暴力法:枚举所有的子串,判断是否为回文串,保存最长的回文串
//时间复杂度O(n^3),空间复杂度O(1).
string LPS_Brute(string s) {
string ans = "";
int max = 0;
int len = s.length();
for (int i = 0; i < len; i++)
for (int j = 1; j <= len - i; j++) {
string test = s.substr(i, j);
if (isP(test) && test.length() > max) {
ans = s.substr(i, j);
max = ans.length();
}
}
return ans;
}
bool isP(string s) {
int len = s.length();
for (int i = 0; i < len / 2; i++) {
if (s.at(i) != s.at(len - i - 1))
return false;
}
return true;
}
//2. 动态规划:动态规划就是将中间的计算结果暂存,减少重复计算的方法,本质上就是空间换时间
//我们用一个数组bool dp[left][right]来表示字符串从left到right这段是否为回文,若dp[left][right]=true,
//要判断dp[left-1][right+1]是否为回文时,只需要判断字符串在left-1和right+1两个未知是否为相同的字符。
// 动态规划的关键:初始状态+状态转移方程:
//初始状态:left=right时,dp[left][right]=true
//状态转移方程:dp[left][right]=true且(left-1)和(right+1)两个未知为相同的字符时,dp[left-1][right+1]=true;
//时间复杂度O(n^2),空间复杂度O(n^2)
string LPS_DP(string s)
{
if (s == "" || s.length() < 2)
return s;
int length = s.length();
int maxS = 0;
int maxE = 0;
int maxLen = 1;
vector<vector<bool>> dp(length, vector<bool>(length));
for (int right = 1; right < length; right++) {
for (int left = 0; left < right; left++) {
if (s.at(left) == s.at(right) && (right - left <= 2 || dp[left + 1][right - 1])) {
dp[left][right] = true;
if (right - left + 1 > maxLen) {
maxLen = right - left + 1;
maxS = left;
maxE = right;
}
}
}
}
return s.substr(maxS, maxLen);
}
//3.中心扩散:因为回文中心的两侧互为镜像,因此可以从它的中心展开,并且只有2n-1个这样的中心
//(因为偶数长度回文子串的中心处于两字母之间)
//时间复杂度O(n^2)[实际上动态规划的复杂度常系数更小],空间复杂度O(1)
int EAC(string s, int left, int right) {
int L = left, R = right;
while (L >= 0 && R < s.length() && s.at(L) == s.at(R)) {
L--;
R++;
}
return R - L - 1;
}
string LPS_EAC(string s) {
if (s == "" || s.length() < 1) return "";
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = EAC(s, i, i);
int len2 = EAC(s, i, i + 1);
int len = max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
string a= s.substr(start, end - start + 1);
return a;
}
};
其实还有一种线性时间复杂度的解法Manacher算法。
深度学习与数学
关注公号,发现更多
长按识别上方二维码关注