本文主要介绍最长回文子序列的动态规划算法及其优化
最长回文子序列: 顾名思义最长回文子序列首先它是一个串的子序列 其次它是回文的 再次它是回文子序列中最长的 在这里要区别于最长回文子串
当然 本问题可以用枚举解决 这里并不叙述 主要讨论一下动态规划来解决
还好了 其实很简单 类似于最长公共子序列 dp[i][j]表示 i->j 的最长回文子序列的长度
所以状态转移方程共有三种情况
1、str[i]==str[j] dp[i][j]=dp[i+1][j-1]+2
2、str[i]!=str[j] dp[i][j]=max(dp[i+1][j],dp[i][j-1])
我们来解释一下状态转移方程
当str[i]==str[j]时 这时 i->j 的最长回文子序列就等以 在 i->j 之间 上一个最长回文子序列的基础上+2
当str[i]!=str[j]时 这时 i->j 的最长回文子序列就等以 i+1->j 和 i->j-1 中最长回文子序列较长的一个
是不是和最长公共子序列类似
然后 我们可以观察一下状态转移方程 第i行的值只与第i+1行有关 所以可以优化一下第一维 我们设置一个标志位每次只保留两行就好了 标志位可初始为0 这样当字符串长度为奇数时 答案在第0行 反之在第1行
代码:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include<list>
#include <bitset>
#include <climits>
#include <algorithm>
#define gcd(a,b) __gcd(a,b)
#define mset(a,n) memset(a,n,sizeof(n)
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w",stdout)
typedef long long LL;
const LL mod=1e9+7;
const int INF=0x3f3f3f3f;
const double PI=acos(-1.0);
using namespace std;
// 时间复杂度 O(n2) 空间复杂度 O(n2)
LL LPS1(char str[],LL n){// 要判断的串 / 字符串长度
LL dp[1000][1000];
memset(dp,0,sizeof(dp));
for (int i=n-1;i>=0;i--){
dp[i][i]=1;
for (int j=i+1;j<n;j++){
if (str[i]==str[j])
dp[i][j]=dp[i+1][j-1]+2;
else
dp[i][j]=max(dp[i][j-1],dp[i+1][j]);
}
}
return dp[0][n-1];
}
// 时间复杂度 O(n2) 空间复杂度 O(n)
LL LPS2(char str[],LL n){// 要判断的串 / 字符串长度
LL dp[2][200000];
memset(dp,0,sizeof(dp));
int flag=0;
for (int i=n-1;i>=0;i--){
dp[flag][i]=1;
for (int j=i+1;j<n;j++){
if (str[i]==str[j])
dp[flag][j]=dp[1-flag][j-1]+2;
else
dp[flag][j]=max(dp[flag][j-1],dp[1-flag][j]);
}
flag=!flag;
}
if (n%2) return dp[0][n-1];
else return dp[1][n-1];
}
int main (){
char str[2000];
cin>>str;
cout<<LPS1(str,strlen(str))<<endl;
cout<<LPS2(str,strlen(str))<<endl;
return 0;
}