最长公共子序列(LCS)

一、什么是最长公共子序列
   
    什么是最长公共子序列呢?举个简单的例子吧,一个数列S,若分别是两个或多个已知序列的子序列,且是所有符合条件序列中最长的,则S称为已知序列的最长公共子序列。

  举例如下,如:有两个随机数列,1 2 3 4 5 6 和 3 4 5 8 9,则它们的最长公共子序列便是:3 4 5。

  一直不明白:最长公共子串和最长公共子序列的区别。
  
   上网查了下,最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别为:子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列;也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。


1.问题描述

给定两个序列X(x1,x2,x3…xm)和Y(y1, y2, y3…yn),求长度最大的公共子序列的长度。


例如:1,5,2,6,8,7 和 2,3,5,6,9,8,4 的LCS为5,6,8(另一个解是2,6,8)

dp[i][j]记录X[1~i]与Y[1~j] 的LCS 的长度 

状态转移方程:dp[i][j] ={dp[i-1][j-1]+1(x[i]==y[j]),max(dp[i-1][j],dp[i][j-1])(x[i]!=y[j])}。

代码1:只能输出长度。

#include<iostream>
using namespace std;
int x[100], y[100], dp[100][100];
int main()
{
	int xn, yn;
	while (cin >> xn >> yn)
	{
		x[100] = y[100] = dp[100][100]  = { 0 };
		for (int i = 1; i <= xn; i++)
		{
			cin >> x[i];
		}
		for (int i = 1; i <= yn; i++)
		{
			cin >> y[i];
		}
		for (int i = 1; i <= xn; i++)
		{
			for (int j = 1; j <= yn; j++)
			{
				if (x[i] == y[j])
				{
					dp[i][j] = dp[i - 1][j - 1] + 1;
				}
				else
				{
					if (dp[i - 1][j] >= dp[i][j - 1])
					{
						dp[i][j] = dp[i - 1][j];
					}
					else
					{
						dp[i][j] = dp[i][j - 1];
					}
				}
			}
		}
		for (int i = 1; i <= xn; i++)
		{
			for (int j = 1; j <= yn; j++)
			{
				cout << dp[i][j] << ' ';
			}
			cout << endl;
		}
		cout << dp[xn][yn] << endl;
	}
	return 0;
}
输出结果

2,输出最长序列中的其中一个。(详细内容介绍请到另一篇转载文章)

代码实现:

#include<iostream>
using namespace std;
int x[100], y[100], dp[100][100], b[100][100];
void LCS(int b[100][100], int x[], int i, int j)
{
	if (i == 0 && j == 0)
	{
		return;
	}
	if (b[i][j] == 1)
	{
		LCS(b, x, i - 1, j - 1);
		cout << x[i];
	}
	else if (b[i][j] == 2)
	{
		LCS(b, x, i - 1, j);
	}
	else
	{
		LCS(b, x, i, j - 1);
	}
}
int main()
{
	int xn, yn;
	while (cin >> xn >> yn)
	{
		x[100] = y[100] = dp[100][100] = b[100][100] = { 0 };
		for (int i = 1; i <= xn; i++)
		{
			cin >> x[i];
		}
		for (int i = 1; i <= yn; i++)
		{
			cin >> y[i];
		}
		for (int i = 1; i <= xn; i++)
		{
			for (int j = 1; j <= yn; j++)
			{
				if (x[i] == y[j])
				{
					dp[i][j] = dp[i - 1][j - 1] + 1;
					b[i][j] = 1;
				}
				else
				{
					if (dp[i - 1][j] >= dp[i][j - 1])
					{
						dp[i][j] = dp[i - 1][j];
						b[i][j] = 2;
					}
					else
					{
						dp[i][j] = dp[i][j - 1];
						b[i][j] = 3;
					}
				}
			}
		}
		for (int i = 1; i <= xn; i++)
		{
			for (int j = 1; j <= yn; j++)
			{
				cout << dp[i][j] << ' ';
			}
			cout << endl;
		}
		cout << "最长公共子序列长度为:" << dp[xn][yn] << endl;
		cout << "最长公共子序列之一为";
		LCS(b, x, xn, yn);
		cout << endl;
	}
	return 0;
}

输出结果:




3,输出所有最长公共子序列(看了几下别人的文章,然后自己写了下,发现虽然能输出所有符合要求的,但是不知道怎么去重,若有大神知道,请告诉下小弟,小弟万分感激)

#include<iostream>
using namespace std;
int x[100], y[100], dp[100][100], b[100][100], result[101];
int n = 0;
//n用于保存解的个数,result数组用于保存解。
void LCS(int b[100][100], int x[], int i, int j, int current_len,int lcs_max_len)
{
	if (i == 0 || j == 0)
	{
		for (int k = 0; k < lcs_max_len;k++)
		{
			cout << result[k];
		}
		cout << endl;
		n++;
		return;
	}
	if (b[i][j] == 1)
	{
		current_len--;
		result[current_len] = x[i];
		LCS(b, x, i - 1, j - 1, current_len, lcs_max_len);
	}
	else if (b[i][j] == 2)
	{
		LCS(b, x, i - 1, j , current_len, lcs_max_len);
	}
	else
	{
		if (b[i][j] == 3)
		{
			LCS(b, x, i , j - 1, current_len, lcs_max_len);
		}
		else
		{
			LCS(b, x, i , j - 1, current_len, lcs_max_len);
			LCS(b, x, i - 1, j , current_len, lcs_max_len);
		}
	}
}
int main()
{
	int xn, yn;
	while (cin >> xn >> yn)
	{
		x[100] = y[100] = dp[100][100] = b[100][100] = { 0 };
		for (int i = 0; i < 100; i++)
		{
			dp[i][0] = dp[0][i] = 0;
		}
		for (int i = 1; i <= xn; i++)
		{
			cin >> x[i];
		}
		for (int i = 1; i <= yn; i++)
		{
			cin >> y[i];
		}
		for (int i = 1; i <= xn; i++)
		{
			for (int j = 1; j <= yn; j++)
			{
				if (x[i] == y[j])
				{
					dp[i][j] = dp[i - 1][j - 1] + 1;
					b[i][j] = 1;
				}
				else
				{
					if (dp[i - 1][j] > dp[i][j - 1])
					{
						dp[i][j] = dp[i - 1][j];
						b[i][j] = 2;
					}
					else if (dp[i - 1][j]<dp[i][j-1])
					{
						dp[i][j] = dp[i][j - 1];
						b[i][j] = 3;
					}
					else
					{
						dp[i][j] = dp[i][j - 1]; //或者dp[i][j]=dp[i-1][j];
						b[i][j] = 4;
					}
				}
			}
		}
		for (int i = 1; i <= xn; i++)
		{
			for (int j = 1; j <= yn; j++)
			{
				cout << dp[i][j] << ' ';
			}
			cout << endl;
		}
		cout << "最长公共子序列长度为:" << dp[xn][yn] << endl;
		{
			if (dp[xn][yn] == 0)//如果不存在公共子序列就退出。
			{
				continue;
			}
		}
		cout << "最长公共子序列为"<<endl;
		LCS(b, x, xn, yn,dp[xn][yn],dp[xn][yn]);
		cout << "共有" << n << "种";
		cout << endl;
	}
	return 0;
}



输出结果:(谁知道怎么去重的告诉小弟一下,万分感激大哭


——————————结束——————————————

由于仅仅学了半年软件开发,所以代码不是特别规范,也不是很简洁,很多东西都是看了别人的文章才能写出来

最后附上给我启发的文章地址:http://blog.chinaunix.net/uid-26548237-id-3374211.html (感谢大神的文章,学习了好多)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值