最长公共子序列

 

最长公共子序列: 两个字符串s,t中公共的,最长的,顺序严格递增的子序列。
 从矩阵推出状态转化方程
 例如: s = "abfcab", t = "abcfba"

  a b f c a b
a 1       1  
b   1       1
c       1    
f     1      
b   1       1
a 1       1  
  a b f c a b
a 1 0 0 0 1 0
b 1 2 2 2 2 2
c 1 2 2 3 3 3
f 1 2 3 3 3 3
b 1 2 3 3 3 4
a 1 2 3 3 4 4

   从这个矩阵中可以看出,最长公共子序列的长度为4, 即形成最长公共子序列的矩阵的对角线的长度。
   本例中最长公共子序列有多个。
   所以状态转化方程为 len[i][j] = max (len[i - 1][j], len[i][j - 1]) (s[i] != t[j])
       len[i][j] = len[i - 1][j - 1] + 1; (s[i] == t[j])
       len[i][j] 表示s中前i个字符和t中前j个字符的最长公共子序列的长度。

源代码

// 常规方法: 时间O(n * n), 空间O(strlen(s) * strlen (t));
#include 
#include 
#define MAX 500
int len[MAX + 1][MAX + 1];
int max_v (int a, int b);
int main()
{
	int i, j;
	char s[MAX + 1], t[MAX + 1];
	while (scanf ("%s%s", s, t) == 2)
	{
		memset (len, 0, sizeof (len));
		for (i = 0; i < strlen (t); i ++)
		{
			for (j = 0; j < strlen (s); j ++)
			{
				if (t[i] == s[j])
				{
					len[i + 1][j + 1] = len[i][j] + 1;
				}
				else
				{
					len[i + 1][j + 1] = max_v (len[i][j + 1], len[i + 1][j]);
				}
			}
		}
		printf ("%d\n", len[strlen (t)][strlen (s)]);
	}
	return 0;
}
int max_v (int a, int b)
{
	if (a > b)
	{
		return a;
	}
	return b;
}
// 空间优化 1  —— 奇偶优化
//  因为len[i][j] 的值只与len[i - 1][j - 1], len[i][j - 1], len[i - 1][j]有关,所以只需要存储len[i - 1], len[i];
//  因此,空间只需要O(2 * min (strlen (s), strlen (t)));
#include 
#include 
#define MAX 500
int len[2][MAX + 1];
int max_v (int a, int b);
int main()
{
	int i, j;
	int index, last;
	char s[MAX + 1], t[MAX + 1];
	while (scanf ("%s%s", s, t) == 2)
	{
		memset (len, 0, sizeof (len));
		for (i = 0; i < strlen (t); i ++)
		{
			index = i % 2;
			last = i % 2 == 0 ? 1 : 0;
			for (j = 0; j < strlen (s); j ++)
			{
				if (t[i] == s[j])
				{
					len[index][j + 1] = len[last][j] + 1;
				}
				else
				{
					len[index][j + 1] = max_v (len[last][j + 1], len[index][j]);
				}
			}
		}
		printf ("%d\n", len[index][strlen (s)]);
	}
	return 0;
}
int max_v (int a, int b)
{
	if (a > b)
	{
		return a;
	}
	return b;
}
// 空间优化2 —— 临时变量保存len[i - 1][j - 1];
// 因此当矩阵中产生一个新行,前一行逐渐被这个新行取代。
// 前一行中为一可能用到的数据就是len[i - 1][j - 1], 用temp1保存,
// 当产生一个新的len[i]时,前一行的len[i]会被替代,所以,在更新len[i]之前需要用temp2 保存len[i],以便传给temp1;
#include 
#include 
#define MAX 500
int len[MAX + 1];
int max_v (int a, int b);
int main()
{
	int i, j;
	int temp1, temp2;
	char s[MAX + 1], t[MAX + 1];
	while (scanf ("%s%s", s, t) == 2)
	{
		memset (len, 0, sizeof (len));
		for (i = 0; i < strlen (t); i ++)
		{
			temp1 = len[0];
			for (j = 0; j < strlen (s); j ++)
			{
				temp2 = len[j + 1];
				if (t[i] == s[j])
				{
					len[j + 1] = temp1 + 1;
				}
				else
				{
					len[j + 1] = max_v (len[j + 1], len[j]);
				}
				temp1 = temp2;
			}
		}
		printf ("%d\n", len[strlen (s)]);
	}
	return 0;
}
int max_v (int a, int b)
{
	if (a > b)
	{
		return a;
	}
	return b;
}


 

你可能感兴趣的:(DP,算法)