目录
在这之前的一些概念:
- 字符子串:指的是字符串中连续的n个字符
- 字符子序列:指的是字符串中不一定连续但先后顺序一致的n个字符,即可以去掉字符串中的部分字符,但不可改变其前后顺序
- 公共子序列:如果序列C既是序列A的子序列,同时也是序列B的子序列,则称它为序列A和序列B的公共子序列
- 最长公共子序列:显然,就是公共子序列中最长的
- 对于一个长度为 n 的序列,它一共有 2^n 个子序列,有 (2^n – 1) 个非空子序列
一、最长上升子序列LIS
Longest Increasing Subsequence
LIS模板
题意:在n个整数中从左到右选出尽量多的整数组成一个上升子序列
例如在1 6 2 3 7 5中,LIS就是1 2 3 5
核心思想:dp[i]代表以i结尾的LIS的长度
#include<iostream>
using namespace std;
const int N=1000; //随便写的大小
int dp[N];
int nums[N];
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>nums[i];
}
int ans=0;
//要求到每个数这里的子序列长度
for(int i=0;i<n;i++){
dp[i]=1;//这样初始化
for(int j=0;j<i;j++){ //那么就在它前边找,看哪个能连上这个数使子序列更长
if(nums[i]>nums[j])//如果能连上
dp[i]=max(dp[i],dp[j]+1); //看连上以j为结尾的子序列会不会更长
ans=max(ans,dp[i]); //更新最长子序列的长度
}
}
cout<<ans;
return 0;
}
洛谷没找到合适的,力扣:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n=nums.size();
vector<int> dp(n,0);
int ans=0;
for(int i=0;i<n;i++){
dp[i]=1;
if(n==1)return 1;
for(int j=0;j<i;j++){
if(nums[j]<nums[i])
dp[i]=max(dp[i],dp[j]+1);
ans=max(dp[i],ans);
}
}
return ans;
}
};
二、最长公共子序列LCS
Longest Common Subsequence
LCS模板
最长公共子序列,就是A和B的公共子序列中最长的(LCS可能不唯一)
用L(x,y)表达A序列的前x项和B序列的前y项的LCS
那么现在考虑最后一项Ax和By
(1)Ax=By
那么它们LCS的最后一项一定是这个元素!
L(x,y)=L(x-1,y-1)+1(2)Ax!=By
设t=L(x,y)最后一项,那么t!=Ax 和 t!=By至少有一个成立(t不能同时等于两个不同的值嘛)
若t!=Ax,那就没有Ax的事
L(x,y)=L(x-1,y)
同理若t!=By
L(x,y)=L(x,y-1)
但我们事先并不知道t,所以我们就去这两种可能中使L大的那个
所以L(x,y)=max(L(x-1,y) ,L(x,y-1))
现在,获得了递推式,还要初始化
x或y=0时L(x,y)=0
总结:
这里还能例证前边的结论:当LCS(x – 1, y) == LCS(x, y – 1)时,其实走哪个分支都一样,虽然长度时一样的,但是可能对应不同的子序列,所以最长公共子序列并不唯一
#include<iostream>
using namespace std;
const int N=1000; //随便写的大小
int dp[N][N];
int text1[N],text2[N];
int main(){
int n1,n2;
cin>>n1;
for(int i=0;i<n1;i++){
cin>>text1[i];
}
cin>>n2;
for(int j=0;j<n2;j++){
cin>>text2[j];
}
int ans=0;
//要求到每个数这里的子序列长度
for(int i=1;i<=n1;i++){
for(int j=1;j<=n2;j++){
if(text1[i-1]==text2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
ans=max(ans,dp[i][j]);
}
}
cout<<ans;
return 0;
}
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
const int M = text1.size();
const int N = text2.size();
vector<vector<int>> dp(M + 1, vector<int>(N + 1, 0));
for (int i = 1; i <= M; ++i) {
for (int j = 1; j <= N; ++j) {
if (text1[i - 1] == text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[M][N];
}
};
三、LCIS最长公共上升子序列
for (int i=1;i<=n;i++)
{
int val=0;//val是决策集合S(i,j)中f[i-1][k]的最大值
for(int j=1;j<=m;j++)
{
//原来的k循环+判断+状态转移
if (a[i]==b[j])
f[i][j]=val+1;
else
f[i][j]=f[i-1][j];
if (b[j]<a[i])
val=max(val,f[i-1][j]);
//j即将增大为j+1,检查j能否进入新的决策集合
}
}
四、不是很正规严谨的总结