解法:区间dp问题(动态规划)
解决思路:
1. 状态表示
dp[ i ][ j ] :表示字符串在区间 [ i , j ] 中,最长回文子序列的长度
2. 状态转移方程
- 如果 i > j ,则区间 [ i , j ] 不合法,故 dp[ i ][ j ] = 0 ;
- 如果 i == j ,则区间 [ i , j ] 表示的是一个字符,一个字符也是回文序列,故 dp[ i ][ j ] = 1;
- 如果 i < j ,则区间 [ i , j ] 合法,这是有有两种情况
- 如果 s[ i ] == s[ j ] ,则说明 s[ i ] 和 s [ j ] 可以加入最长回文子序列,则 dp[ i ][ j ] = dp[ i + 1 ] [ j - 1 ] + 2;
- 如果 s[ i ] != s[ j ] ,则说明 s[ i ] 和 s [ j ] 不能同时选了,可以退而求其次,在[ i , j - 1 ] 区间中去求最长回文子序列,即 dp[ i ] [ j - 1 ],在[ i + 1 , j ] 区间中去求最长回文子序列,即 dp[ i + 1 ] [ j ],我们需要求出它们两个之间的最大值,即 dp[ i ][ j ] = max( dp[ i ] [ j - 1 ] , dp[ i + 1 ] [ j ] );
3. 初始化
其实不用管,因为我们在填表的时候需要的值是当前位置的左边,左下,下变,这三个值,当时因为当 i == j 时,我们会填表为 1,而在dp表的左下半部分是基本用不到的(除了临近i==j的位置,需要用它求max),所以我们填表只需要将全部初始化为0即可。
4. 填表顺序
从下向上,从左向右。因为用到的值是当前位置的左边,左下,下变,这三个值,必须保证这三个位置有值。
5. 结果
整个字符串为 [ 0, s.size() - 1 ] 的区间,所以答案为 dp[ 0 ][ s.size() - 1 ] 。
#include <iostream>
#include <string>
using namespace std;
const int N = 1e3 + 10;
int dp[N][N]; // 1.创建dp表+初始化(初始化为0就可以了)
string s;
int main()
{
cin >> s;
int n = s.size();
// 2. 填表
for(int i = n - 1; i >= 0; i--)
{
for(int j = i; j < n; j++)
{
if(i == j) dp[i][j] = 1;
else if(i < 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]);
}
else dp[i][j] = 0;
}
}
// 3.输出结果
cout << dp[0][n - 1];
return 0;
}

京公网安备 11010502036488号