适用题型:
Description:
给定两个序列A,B,其长度分别为N,M,现在要你求出这两个序列中最长的一个公共子序列,并使这个子序列递增。比如数列{1,2,0,4,5}和{1,0,4,5,2},其最长公共上升子序列就是{1,4,5},长度为3。
Input:
第一行:一个N;
第二行:N个数,为数列A;
第三行:一个M;
第四行:M个数,为数列B。
Output:
一个数,为最长公共上升子序列的长度。
Sample Input:
5 1 2 0 4 5 5 1 0 4 5 2
Sample Output:
3
主要思路:每次找a[i]==b[j],(i 1-n,j 1-m)然后查找以前的最长的公共上升子序列的长度,更新一下。
最简单的就是设f[i][j]表示以a[i],b[j](这里a[i]==b[j]),结尾的最长公共子序列长度,O(n^2*m^2)。因为每次得往后找另一对i,j。总共找两对i,j。
这里为了简化可以设f[j]表示以b[j]结尾的最长公共子序列,每次找a[i]==b[j],然后比较f[j]与前面出现的最大的f[k]+1比较。这里还有个问题就是得存k的值,而且得注意每次对应一个a[i],应该是k从0开始,j 是从1开始
二维数组的代码:
#include<iostream>
#include<string.h>
#define maxn 100
using namespace std;
void fun(char *A,int m,char *B,int n,int a[][maxn],int b[][maxn])
{
for(int i=0;i<=m;i++) a[i][0]=0;
for(int i=0;i<=n;i++) a[0][i]=0;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(A[i-1]==B[j-1])
{
a[i][j]=a[i-1][j-1]+1;
b[i][j]=0;
}
else if(a[i-1][j]>a[i][j-1])
{
a[i][j]=a[i-1][j];
b[i][j]=1; //行增加,为1
}
else
{
a[i][j]=a[i][j-1];
b[i][j]=-1; //列增加,为-1
}
}
}
}
void print(char *A,int m,char *B,int n,int b[][maxn])
{
if(m==0||n==0) return ;
else if(b[m][n]==0)
{
print(A,m-1,B,n-1,b);
cout<<A[m-1];
}
else if(b[m][n]==1)
{
print(A,m-1,B,n,b);
}
else
{
print(A,m,B,n-1,b);
}
}
int main()
{
char A[maxn],B[maxn];
int a[maxn][maxn]; //记录每个状态的最大字串长
int b[maxn][maxn]; //记录前驱
int m,n;
cin>>A>>B;
m=strlen(A),n=strlen(B);
fun(A,m,B,n,a,b);
cout<<a[m][n]<<endl;
print(A,m,B,n,b);
return 0;
}
代码:
#include<cstdio>
#define MAXN 1000
int n,m;
int a[MAXN],b[MAXN],f[MAXN];
//f[j]表示以j结尾最大值
void dp()
{
for(int i=1; i<=n; i++)
{
int k=0;//设置k从0开始,而i,j都是从1开始的,防止第一组相同的数没有被处理
for(int j=1; j<=m; j++)
{
if(a[i]==b[j])
{
if(f[j]<f[k]+1)
f[j]=f[k]+1;
}
if(a[i]>b[j])//下次比较比a[i]小的数的f值,所以每次遇到比a[i]小的数都得更新小比a[i]小的最大f值
{
if(f[j]>f[k])
k=j;
}
}
}
}
int main()
{
memset(f,0,sizeof(f));
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1; i<=m; i++)
scanf("%d",&b[i]);
dp();
int max=-1;
for(int i=1; i<=m; i++)
{
if(max<f[i])
max=f[i];
}
printf("%d\n",max);
return 0;
}