#include <iostream>
#include <string>
#include <vector>
#include <algorithm> // 用于max函数
using namespace std;
int main()
{
string str;
cin >> str; // 读取输入字符串
int n = str.size(); // 获取字符串长度
vector<vector<int>> dp(n, vector<int>(n, 0)); // 创建二维数组dp,dp[i][j]表示str[i..j]的最长回文子序列长度
// 初始化:单个字符的最长回文子序列长度为1
for(int i = 0; i < n; i++)
dp[i][i] = 1;
// 按子串长度从小到大遍历,从长度2开始,直到整个字符串
for(int len = 2; len <= n; len++)
{
// 遍历所有可能的起始索引i
for(int i = 0; i + len <= n; i++)
{
int j = i + len - 1; // 计算对应的结束索引
// 如果当前子串的首尾字符相同
if(str[i] == str[j])
dp[i][j] = dp[i + 1][j - 1] + 2; // 长度为中间部分的长度加2
// 如果首尾字符不同
else
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); // 取舍弃首字符或尾字符后的最大值
}
}
// 输出整个字符串的最长回文子序列长度
cout << dp[0][n - 1] << endl;
return 0;
}
题目分析:最长回文子序列(Longest Palindromic Subsequence)
这道题要求我们找到给定字符串中的最长回文子序列的长度。回文子序列是指从原字符串中删除 0 个或多个字符后,剩余字符按原顺序排列形成的字符串,且该字符串正读和反读都相同。例如,字符串 "bbbab" 的最长回文子序列是 "bbbb",长度为 4。
解题思路:动态规划
1. 问题分解与状态定义
动态规划的核心是将原问题分解为更小的子问题,并存储子问题的解以避免重复计算。对于这道题,我们定义状态:
dp[i][j]表示字符串从索引i到索引j的子串中,最长回文子序列的长度。
2. 状态转移方程
对于子串 str[i..j],我们考虑以下两种情况:
- 如果 str[i] == str[j]:两端字符可以同时包含在回文子序列中,因此 dp[i][j] = dp[i+1][j-1] + 2。例如,对于子串 "abba",由于首尾字符都是 'a',其最长回文子序列长度为中间子串 "bb" 的长度加 2。
- 如果 str[i] != str[j]:两端字符不能同时包含,因此我们需要舍弃一端,取两种情况中的最大值:dp[i][j] = max(dp[i+1][j], dp[i][j-1])。例如,对于子串 "abca",由于首尾字符不同,我们可以选择包含首字符 'a' 并检查子串 "bc"(即 dp[i][j-1]),或者包含尾字符 'a' 并检查子串 "bc"(即 dp[i+1][j]),取两者中的最大值。
3. 初始化条件
- 对于单个字符,其最长回文子序列长度为 1,即
dp[i][i] = 1。
4. 遍历顺序
为了确保在计算 dp[i][j] 时,所有依赖的子问题(如 dp[i+1][j-1]、dp[i+1][j]、dp[i][j-1])已经被解决,我们按子串长度从小到大遍历:
- 先处理长度为 2 的子串,再处理长度为 3 的子串,以此类推,直到处理整个字符串。
- 对于每个长度
len,遍历所有可能的起始索引i,计算对应的结束索引j = i + len - 1。

京公网安备 11010502036488号