杭电1159————LCS(最长公共子序列)

Common Subsequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 24225    Accepted Submission(s): 10707


Problem Description
A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = <x1, x2, ..., xm> another sequence Z = <z1, z2, ..., zk> is a subsequence of X if there exists a strictly increasing sequence <i1, i2, ..., ik> of indices of X such that for all j = 1,2,...,k, xij = zj. For example, Z = <a, b, f, c> is a subsequence of X = <a, b, c, f, b, c> with index sequence <1, 2, 4, 6>. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y. 
The program input is from a text file. Each data set in the file contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct. For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line. 
 

Sample Input
  
  
abcfbc abfcab programming contest abcd mnp
 

Sample Output
  
  
4 2 0
 
  
  



题目描述很简单,就是求最长公共子序列(longest common substrings_—— LCS)
刚好介绍一下最长公共子序列的求法(资料摘自————http://v.163.com/movie/2010/12/L/4/M6UTT5U0I_M6V2U1HL4.html)


假设:
x序列为   A B C D C B A
y序列为   B C D E A C B A
那么最长公共子序列应该是 : BCDCBA(不必是连续的)不知道LCS的算法的时候,我们是用的爆搜的方式。
1.爆搜法
找到x和y中的所有子串,然后依次比较,选取长度最大的那个。这个算法估计是大多数人的想法,但是这个时间复杂度实在是太高了。
x中的子串有 2^7  y中的子串有 2^8个 在考虑比较的时间,也就是总时间为O(2^15),天文数字。
所以说爆搜肯定是不会用的。
2.递归法(建造一颗递归树,自上而下的寻找)
这个思想的基础是如下伪代码

<span style="font-family:Microsoft YaHei;font-size:14px;">if(x[i] == y[j])
	then  c[i][j] = LCS(c[i-1][j-1]) + 1
else
	c[i][j]  = max(LCS(c[i-1][j]),LCS(c[i][j-1])
PS:其中c[i][j]表示的是x的前i个字符和y的前j个字符中LCS的长度。
</span>


伪代码的正确性显而易见

很显然,这段伪代码中最耗费时间的是max(LCS(c[i-1][j]),LCS(c[i][j-1])这一句,我们把它的递归树划出就明白我的意思了
(x,y长度分别为7和8)



  
  
这棵递归树里边有很多重复的地方(虚线框起来)如果没有优化,这种递归方式实际上比爆搜还慢....(因为树的高度是7+8),然而有很多重复的地方,即满足重叠子问题这一要求,很显然伪代码也给出了这个问题的最优子结构,因此我们就可以用动态规划的思想来解决这个问题
在刚才伪代码的基础上更改一下,便是:
if(c[i][j] == NULL)
	then 
		if(x[i] == y[j])
				then  c[i][j] <-- LCS(c[i-1][j-1]) + 1
		else
				c[i][j]  <-- max(LCS(c[i-1][j]),LCS(c[i][j-1])
else
	return c[i][j]

模拟一下算法的实现方式:



其中箭头标注的就是 c[i][j] = LCS(c[i-1][j-1])+1这一条语句。
最后附上这一题的代码:
(杭电亲测)
#include <stdio.h>
#include <string.h>
#define Max(A,B) ( (A) > (B) ? (A) : (B) )
#define maxn 1005

int dp[maxn][maxn];
char s1[2 * maxn],s2[2 * maxn];
void LCS(int len1,int len2)
{
	for(int i = 1 ; i <= len1 ; i++)
	{
		for(int j = 1 ; j <= len2 ; j++)
		{
			if(s1[i] == s2[j])
				dp[i][j] = dp[i-1][j-1] + 1;
			else
				dp[i][j] = Max(dp[i-1][j],dp[i][j-1]);
		}
	}
}
int main()
{
	memset(s1,0,sizeof(s1));
	memset(s2,0,sizeof(s2));
	while(scanf("%s %s",s1 + 1,s2 + 1) != EOF)
	/*此处为何要+1呢是因为循环的时候是从1 to len,我们想让是s1[0] s2[0] 是0
	不存字符,因此从s1[1] s2[1]地址存数据*/ 
	{
		memset(dp,0,sizeof(dp));
		int len1 = strlen(s1 + 1);
		int len2 = strlen(s2 + 1);
		LCS(len1,len2);
		printf("%d\n",dp[len1][len2]);
		memset(s1,0,sizeof(s1));
		memset(s2,0,sizeof(s2));
	}
	return 0;
}



 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值