最长公共子串( Longest Common subString),简化成两个串的情况,就是要求两个找出两个字符串A、B相同的子串中最长的一个,要求连续。这和最长公共子序列不同同,最长公共子序列可以不连续。
算法:
找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的。其实这又是一个序贯决策问题,可以用动态规划来求解。我们采用一个二维矩阵来记录中间的结果。这个二维矩阵怎么构造呢?直接举个例子吧:"bab"和"caba"(当然我们现在一眼就可以看出来最长公共子串是"ba"或"ab")
b a b
c 0 0 0
a 0 1 0
b 1 0 1
a 0 1 0
我们看矩阵的斜对角线最长的那个就能找出最长公共子串。
不过在二维矩阵上找最长的由1组成的斜对角线也是件麻烦费时的事,下面改进:当要在矩阵是填1时让它等于其左上角元素加1。
b a b
c 0 0 0
a 0 1 0
b 1 0 2
a 0 2 0
这样矩阵中的最大元素就是 最长公共子串的长度。
算法还可以改进,我们可以将查找最大长度和对应字符的工作放在构造矩阵的过程中完成,一边构造一边记录当前的最大长度和对应位置,这样就节省了n^2的查找时间。
空间上也可以做改进,如果按照如上的方式构造,我们发现,当矩阵的第i+1行的值计算完成后,第i行的值就没有用了,即便是最长的长度出现在第i行,我们也已经用变量记录下来了。因此,可以将矩阵缩减成一个向量来处理,向量的当前值对应第i行,向量的下一个循环后的值对应第i+1行。
注意:内层循环从后往前,因为c[j]依赖于上一次的c[j-1];
技巧:c的长度比内层循环长度大1,设置c[0]=0,避免了判断内存循环字符0位置的特殊处理。
Code:
最长公共子串(连续) LCS
//已知两个串,找出它们的最长公共子串。
//Sample Input:
//SULAODASHANGBINGHAIHAIGAN BINGHAIZHANGFESUDAFVGTPWH
//Sample Output:
//BINGHAI
//输入是从标准输入读取,输出打印到标准输出之中
#include <iostream>
#include <cstring>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=1000;
int LCStr(char *str1,int len1,char *str2,int len2,char **lcstr){
int i,j;
int *c=new int[len2+1];
int maxcount=0,maxindex=0;
memset(c,0,sizeof(int)*len2);
for(i=0;i<len1;i++){
for(j=len2;j>0;j--){//current count from len2 to 1;
if(str1[i]==str2[j-1]){
c[j]=c[j-1]+1;
if(c[j]>=maxcount){
maxcount=c[j];
maxindex=j-1;//the current index of str2 is j-1;
}
}else
c[j]=0;
}
}
if(maxcount==0)
return 0;
*lcstr=new char[maxcount+1];
for(i=0;i<maxcount;i++)
(*lcstr)[i]=str2[maxindex-maxcount+1+i];
(*lcstr)[i]='\0';
return maxcount;
}
int main()
{
char str1[N];
char str2[N];
while(scanf("%s%s",&str1,&str2)!=EOF){
int len1 = strlen(str1);
int len2 = strlen(str2);
char *lcs=NULL;
int len = LCStr(str1 , len1 , str2 , len2 , &lcs);
for(int i = 0 ; i < len ; ++i)
printf("%c",lcs[i]);
printf("\n");
}
}
参考:
华夏35度,总结了子串的几种情况:http://www.cnblogs.com/zhangchaoyang/articles/2012070.html
比较好的范例:http://blog.csdn.net/steven30832/article/details/8260189