题目:给定两个字符串,求它们的最长公共子序列和最长公共子串
思路:都是根据动态规划的思想,最长公共子序列是不连续,而最长公共子串是连续的
例子: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;
}