最长公共子串问题,较为常见的思路是动态规划吧,至于后缀数组等更高效的算法,本人菜鸟一个,理解能力有限啊。直接上解决思路。
问题解决思路:给定两个字符串s,t,求s,t的最长公共子串。假设最长公共子串的确包括s[i],t[j],并假设lcs[i,j]表示以字符串s的第i个字符,t的第j个字符结尾并包含s[i],t[j]的最长公共子串的长度,那么,就有如下的DP方程:
根据上述方程,就很容易写出最长公共子串的代码了。同时,为了输出最后最长公共子串,程序中可记录最长公共子串的位置,这样就得到结果了。该代码时间复杂度和空间复杂都是O(nm),n,m分别是s,t的长度。当然,空间复杂度可以更低。DP的话,解决该问题的时间复杂度就是0(nm)。后缀数组能够降低时间复杂度,好像可以到线性的时间复杂度。留着日后有时间再慢慢研究后缀数组的解法吧。
<span style="font-size:18px;">#include<iostream>
using namespace std;
int LCSubstr(const char *s,int sLen,const char*t,int tLen)
{
if(NULL==s||NULL==t)
{
cout<<"Empty str"<<endl;
return 0;
}
int **lcs;
lcs = new int*[sLen];
for(int i=0;i<sLen;i++)
lcs[i]=new int[tLen];
lcs[0][0]=0;
for(int i=0;i<sLen;i++)
{
if(s[i]==t[0])
lcs[i][0]=1;
else
lcs[i][0]=0;
}
for(int j=0;j<tLen;j++)
{
if(s[0]==t[j])
lcs[0][j]=1;
else
lcs[0][j]=0;
}
int maxLen;
int idx=-1;
maxLen=s[0]==t[0]?1:0;
for(int i=1;i<sLen;i++)
{
for(int j=1;j<tLen;j++)
{
if(s[i]==t[j])
{
lcs[i][j]=lcs[i-1][j-1]+1;
if(lcs[i][j]>maxLen)
{
maxLen = lcs[i][j];
idx = j;
}
}
else
{
lcs[i][j]=0;
}
}
}
for(int i=0;i<sLen;i++)
{
for(int j=0;j<tLen;j++)
cout<<lcs[i][j]<<" ";
cout<<endl;
}
//输出最长公共子串
cout<<s<<"和"<<t<<"最长公共子串是:"<<endl;
if(idx==-1)
{
cout<<"";
}
else
{
for(int i=idx-maxLen+1;i<=idx;i++)
cout<<t[i];
}
cout<<endl;
for(int i=0;i<sLen;i++)
delete[] lcs[i];
delete []lcs;
return maxLen;
}
int main()
{
char *s="adcdef";
char *t="df";
int sLen=strlen(s);
int tLen=strlen(t);
int x =LCSubstr(s,sLen,t,tLen);
cout<<"最长公共字串长度是:"<<x<<endl;
return 0;
}</span>
注:若两个字符串存在多个长度一样的最长公共子串,本程序只能返回其中一个,不能都返回。解决方案是:可再遍历以LCS矩阵,对其中多个最大的相同数字,从该位置起,按斜对线往左上方输出,直到遇到LCS的元素为0为止。有什么更好的办法,请指出来啊。
测试过一些用例,能至少通过题目所给出的所有用例,但不知源代码中是否仍然存在bug,或者这个思路完全有问题,欢迎指正哈。