文章目录
二维动态规划-子序列问题
相似解法
https://blog.csdn.net/weixin_43892514/article/details/140418546?spm=1001.2014.3001.5501
LeetCode5. [最长回文子串]
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a”
输出:“a”
示例 4:
输入:s = “ac”
输出:“a”
方法一:动态规划
class Solution {
public:
string longestPalindrome(string s) {
int start = 0;
int length = 0;
int maxLen = 1; // 根据题意决定
int n = s.length();
// dp[i][j]: s[i, j]的子串是否为回文串
vector<vector<int>> dp(n, vector<int>(n, false));
// 边界值, 一个字符必为回文串,对角线
for (int i = 0; i < n; i++) {
dp[i][i] = true;
}
// 反着遍历保证正确的状态转移
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 1; j < n; j++) {
int d = j - i + 1;
// 状态转移方程
if (s[i] == s[j]) {
if (d == 2) { // 确保在上三角:行 < 列
dp[i][j] = true; // base case
} else {
dp[i][j] = dp[i + 1][j - 1];
}
} else {
dp[i][j] = false;
}
if (dp[i][j] == true && d > maxLen) {
start = i;
maxLen = d;
}
}
}
// 整个 s 的最长回文子串长度
return s.substr(start, maxLen);
}
};
方法二:双指针
如果回文串的长度为奇数,则它有一个中心字符;如果回文串的长度为偶数,则可以认为它有两个中心字符。
class Solution {
public:
string palindrome(string& s, int left, int right)
{
while (left >= 0 && right < s.size() && s[left] == s[right]) {
--left;
++right;
}
return s.substr(left + 1, right - left - 1); // 返回以 s[left] 和 s[right] 为中⼼的最⻓回⽂串
}
string longestPalindrome(string s) {
string res;
for (int i = 0; i < s.size(); i++) {
string s1 = palindrome(s, i, i); // 以i为中心的回文子串
string s2 = palindrome(s, i, i + 1); // 以i和i+1为中心的子串
res = (res.size() > s1.size()) ? res : s1;
res = (res.size() > s2.size()) ? res : s2;
}
return res;
}
};
C++17尝鲜:结构化绑定声明(Structured Binding Declaration)
class Solution {
public:
pair<int, int> palindrome(string& s, int left, int right)
{
while (left >= 0 && right < s.size() && s[left] == s[right]) {
--left;
++right;
}
return {left + 1, right - 1}; // 返回以 s[left] 和 s[right] 为中⼼的最⻓回⽂串
}
string longestPalindrome(string s) {
string res;
int start = 0;
int end = 0;
for (int i = 0; i < s.size(); i++) {
auto [left1, right1] = palindrome(s, i, i); // 以i为中心的回文子串
auto [left2, right2] = palindrome(s, i, i + 1); // 以i和i+1为中心的子串
if (right1 - left1 > end - start) {
start = left1;
end = right1;
}
if (right2 - left2 > end - start) {
start = left2;
end = right2;
}
}
return s.substr(start, end - start + 1);
}
};
LeetCode516 最长回文子序列
class Solution {
public:
int longestPalindromeSubseq(string s) {
int n = s.size();
// dp[i][j]: s[i, j]子序列的最长回文子串长度为dp[i][j]
// i <= j
vector<vector<int>> dp(n, vector<int>(n));
// base case
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}
// dp[i][j] = dp[i + 1][j - 1]; 遍历方向要确保每个子问题都已经算好了
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 1; j < n; j++) {
if (s[i] == s[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1];
}
};
Leetcode 1312. 让字符串成为回文串的最少插入次数
给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。
请你返回让 s 成为回文串的 最少操作次数 。
「回文串」是正读和反读都相同的字符串。
示例 1:
输入:s = “zzazz”
输出:0
解释:字符串 “zzazz” 已经是回文串了,所以不需要做任何插入操作。
示例 2:
输入:s = “mbadm”
输出:2
解释:字符串可变为 “mbdadbm” 或者 “mdbabdm” 。
示例 3:
输入:s = “leetcode”
输出:5
解释:插入 5 个字符后字符串变为 “leetcodocteel” 。