最长公共子序列和最长公共子串

本文详细介绍了使用动态规划方法求解两个字符串的最长公共子序列和最长公共子串的过程,包括算法思路、具体实现及示例说明。

题目:给定两个字符串,求它们的最长公共子序列和最长公共子串

思路:都是根据动态规划的思想,最长公共子序列是不连续,而最长公共子串是连续的

例子:str1 = "acaccbabb" ,str2 = "acbac"

求解最长公共子序列时为二维数组的最后一个元素,递推公式为


求解最长公共子串时,只有前面一个相等的时候子串长度才会加1,不等的时候就变为了0,因此为二维数组的最长斜线的长度,也就是最长斜线的最后一个数即为最长子串长度,递推公式为



代码为:

#include <iostream>
using namespace std;

int LongestCommonSubString(const char *str1  , int len1 , const char *str2 , int len2 , char *&lcs)  
{  
	if(NULL == str1 || NULL == str2)  
	{  
		return -1;  
	}  

	int f[20][20] = {0};

	int max_len = 0;    //匹配的长度  
	int pos = 0;        //在str2上的匹配最末位置  
	for(int i = 1 ; i <= len1 ; ++i)  
	{  
		for(int j = 1 ; j <= len2 ; ++j)  //更新时从后往前遍历  
		{   
			if(str1[i-1] == str2[j-1])  
			{  
				f[i][j] = f[i-1][j-1] + 1;  
				if(f[i][j] > max_len)  
				{  
					max_len = f[i][j];  
					pos = j-1;  
				}  
			}  
			else  
			{  
				f[i][j] = 0;  
			}  
		}  
	}  

	if (max_len == 0)
	{
		return 0;
	}

	//  得到的公共子串  
	lcs = new char[max_len];  
	for(int i = 0 ; i < max_len ; ++i)  
	{  
		lcs[i] = str2[pos-max_len+1+i];  
	}    

	return max_len;  

}

//最长公共子序列

int LongestCommonSubsequence(const char *str1  , int len1 , const char *str2 , int len2 , char *&lcs)
{
	if(NULL == str1 || NULL == str2)  
	{  
		return -1;  
	} 

	int f[20][20] = {0};

	int max_len = 0;    //匹配的长度 

	for (int i = 1; i <= len1; ++i)
	{
		for (int j = 1; j <= len2; ++j)
		{
			if (str1[i-1] == str2[j-1])
			{
				f[i][j] = f[i-1][j-1]+1;
			}
			else if (f[i-1][j] <= f[i][j-1])
			{
				f[i][j] = f[i][j-1];
			}
			else
			{
				f[i][j] = f[i-1][j];
			}
		}
	}

	max_len = f[len1][len2];

	if (max_len == 0)
	{
		return 0;
	}

	//  得到公共子串  
	lcs = new char[max_len]; 
	int k = max_len-1;

	for (int i = len1,j = len2; i >= 1 && j >= 1;)
	{
		if (str1[i-1] == str2[j-1])
		{
			lcs[k--] = str1[i-1];
			i--;
			j--;
		}
		else if (f[i-1][j] <= f[i][j-1])
		{
			j--;
		}
		else
		{
			i--;
		}
	}

	return max_len;
}

int main()
{
	const char *text = "acaccbabb"; 
	const char *query = "acbac";  
	int len1 = strlen(text);  
	int len2 = strlen(query);  

	char *lcs = NULL;  

	int len = LongestCommonSubString(text , len1 , query , len2 , lcs);
	//int len = LongestCommonSubsequence(text , len1 , query , len2 , lcs);
	cout<<"max length = "<<len<<endl;  

	for(int i = 0 ; i < len ; ++i)  
	{  
		cout<<lcs[i]<<" ";  
	}  
	cout<<endl;

	if (lcs)
	{
		delete [] lcs;
	}
}

在求最长公共子串的时候可以不用二维数组表示,只申请一个一维数组就可以,可以在空间复杂度上将为O(n),其中n为两个字符串中最短的那一个,方法来自于 steven30832同学的博客,非常感谢。。。

代码为:

int LongestCommonSubString_solution2(const char *str1  , int len1 , const char *str2 , int len2 , char *&lcs)  
{  
	if(NULL == str1 || NULL == str2)  
	{  
		return -1;  
	}  
  
	int *c = new int[len2+1];  
	for(int i = 0 ; i < len2 ; ++i)  
	{  
		c[i] = 0;  
	}  
	int max_len = 0;    //匹配的长度  
	int pos = 0;        //在str2上的匹配最末位置  
	for(int i = 0 ; i < len1 ; ++i)  
	{  
		for(int j = len2 ; j > 0 ; --j)  //更新时从后往前遍历  
		{   
			if(str1[i] == str2[j-1])  
			{  
				c[j] = c[j-1] + 1;  
				if(c[j] > max_len)  
				{  
					max_len = c[j];  
					pos = j-1;  
				}  
			}  
			else  
			{  
				c[j] = 0;  
			}  
		}  
	}  

	if(0 == max_len)  
	{  
		delete [] c;  
		return 0;  
	}  

	//  得到公共子串  
	lcs = new char[max_len];  
	for(int i = 0 ; i < max_len ; ++i)  
	{  
		lcs[i] = str2[pos-max_len+1+i];  
	}  
	  
	delete [] c;  
	  
	return max_len;  

}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值