一、问题描述:
二、状态空间:
三、状态转移方程:
四、实现方法:
由于f(i,j)只和f(i-1,j-1), f(i-1,j)和f(i,j-1)有关, 而在计算f(i,j)时, 只要选择一个合适的顺序,就可以保证这三项都已经计算出来了,这样就可以计算出f(i,j)。这样一直推到f(n, m)就得到所要求的解了。
状态空间的定义方法:
一般二维数组(n*m)实现(可能会爆空间);
滚动数组实现,只需要存储两行数据(2*n)。复杂度:O(n*m)
五、LCS思考
1、如果要求最长公共子序列的个数,该如何处理?
设g(i, j)为第一个字符串的前i个字符和第二个字符串的前j个字符达到最长公共子串的长度(f(i, j),设为k)的字符串的个数,则有:
如果f(i-1, j)=k,则g(i, j) += g(i-1, j)
如果f(i, j-1)=k,则g(i, j) += g(i, j-1)
如果a(i)=b(j),则g(i, j) += g(i-1, j-1)
如果a(i)!=b(j), 且f(i-1,j-1)=k,则g(i, j) -= g(i-1, j-1)
最终结果为:g(n, m)2、如果要输出一个最长公共子序列,又该如何处理?
方法:x从1到k,扫描f(i, j)即可。
如果要输出所有的最长公共子序列,又该如何处理?
方法:对f(i, j)用回溯法,从右下角开始。
如果a[i]=b[j],则a[i]为最长公共子序列的一个元素,下一步为(i-1, j-1)
如果a[i]!=b[j],下一步为f(i-1, j)和f(i,j-1)较大的方向。如果两者相等,则两个方向都要考虑。
直到i或j等于0为止。
六、LCS优化
举例说明:设有两个字符串:
X:abdba
Y:dbaaba
先顺序扫描X串,取其在Y串的所有位置:
a(2,3,5) b(1,4) d(0)。
将每个字母的位置反序列,并替换X中的对应的字母,替换结果(5,3,2) (4,1) (0) (4,1) (5,3,2)
则最终每个括号里选一个数构成的最长严格递增子序列的长度即为解。
因此最大长度为3。
七、实现代码
1.二维数组实现(HDU1159 Common SubSequence)
#include
#include
#include
#include
#include
#include
using namespace std;
/*
编程人:zmh
编程日:2018.1.24
编程题:求两个序列的最长公共子序列
编程思路:f(i, j)表示第一个字符串的前i个字符和第二个字符串的前j个字符的最长公共子串的长度
算法没有优化:
状态转移方程:f[i][j] = f[i-1][j-1] + 1 (a[i] == b[i])
f[i][j] = MAX(f[i-1][j],f[i][j-1]) (a[i] != b[i])
输入样例:
abcfbc abfcab
programming contest
abcd mnp
输出样例:
4
2
0
AC
*/
#define N 1001
#define MAX(a,b) ((a)>(b)?(a):(b))
string a,b;
int lena,lenb;
int f[N][N];
int main(){
while(cin>>a>>b){
lena = a.length();
lenb = b.length();
memset(f,0,sizeof(f));
for(int i=0;i
2.滚动数组空间优化(HDU1159 Common SubSequence)
#include
#include
#include
#include
#include
#include
using namespace std;
/*
编程人:zmh
编程日:2018.1.24
编程题:求两个序列的最长公共子序列
编程思路:f(i, j)表示第一个字符串的前i个字符和第二个字符串的前j个字符的最长公共子串的长度
算法优化:使用 2*N 滚动数组对其进行空间优化
状态转移方程:f[i][j] = f[i-1][j-1] + 1 (a[i] == b[i])
f[i][j] = MAX(f[i-1][j],f[i][j-1]) (a[i] != b[i])
*/
//这个数组开的大小导致 WR
#define N 1005
#define MAX(a,b) ( (a)>(b)?(a):(b) )
int dp[2][N];
string a,b;
int main(){
while(cin>>a>>b){
memset(dp,0,sizeof(dp));
int lena = a.length();
int lenb = b.length();
int t = 0;
for(int i=0;i
3.最长公共子序列的输出
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 1001
string a,b,ab;
int lena,lenb,lenab;
int dp[N][N];
int vis[N][N];
/*
输出一个公共子序列,只要扫描f[i][j],输出每一行第一个变的位置对应的a序列的字符
*/
void dfs1(int la,int lb){
int t,i,j;
for(t = 1;t<=dp[la][lb];t++){
for(i=1;i<=la;i++){
for(j=1;j<=lb;j++){
if(dp[i][j] == t){
cout<>a>>b){
lena = a.length();
lenb = b.length();
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
for(int i=0;i<=lena;i++)
vis[i][0] = 1;
for(int i=0;i<=lenb;i++)
vis[0][i] = 2;
for(int i=1;i<=lena;i++){
for(int j=1;j<=lenb;j++){
if(a[i-1] == b[j-1]){
dp[i][j] = dp[i-1][j-1] + 1;
vis[i][j] = 0;
}else{
if(dp[i-1][j]>dp[i][j-1]){
dp[i][j] = dp[i-1][j];
vis[i][j] = 1;
}else{
dp[i][j] = dp[i][j-1];
vis[i][j] = 2;
}
}
}
}
dfs1(lena,lenb);//输出一个公共子串
cout<
4.LCS的应用,最长公共子序列的输出(HDU1503 Advanced Fruits)
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 1001
string a,b,ab;
int lena,lenb,lenab;
int dp[N][N];
int vis[N][N];
void dfs2(int la,int lb){
if(!la&&!lb){//此处导致一直运行不对
return;
}
if(vis[la][lb] == 1){
dfs2(la-1,lb);
cout<>a>>b){
lena = a.length();
lenb = b.length();
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
for(int i=0;i<=lena;i++)
vis[i][0] = 1;
for(int i=0;i<=lenb;i++)
vis[0][i] = 2;
for(int i=1;i<=lena;i++){
for(int j=1;j<=lenb;j++){
if(a[i-1] == b[j-1]){
dp[i][j] = dp[i-1][j-1] + 1;
vis[i][j] = 0;
}else{
if(dp[i-1][j]>dp[i][j-1]){
dp[i][j] = dp[i-1][j];
vis[i][j] = 1;
}else{
dp[i][j] = dp[i][j-1];
vis[i][j] = 2;
}
}
}
}
dfs2(lena,lenb);
cout<