求两个字符串的最大公共子串,可以通过动态规划,很容易解决。但是遇到n个字符串的最大公共子串,就比较复杂了,网上都说需要通过后缀数组来解决,这个方法还在研究中,我先用暴力解法来跑跑,看看运行效率如何。
这里解决过程中容易产生一种错误的解题思路,即我之前想的一些想法
就是先求两个字符串的最大公共子串,然后利用该公共子串和其他的字符串进行对比,求解他们之间的最大公共子串。但这是就出现了一个严重的错误
假设串1 为abc 串2 为abcd 串3 为c
那么先求解 串1和串2的最大公共子串为ab 但是和串3在一求最大公共子串,就出现问题了,没有公共子串,这是错误的,实际上c为最大公共子串。
这和求几个数的最大公约数不同,我错误的认为,局部的解可以得到全局的解,这是性质上的不同。
后面再继续分析后缀数组,真的有些力不从心,有点小复杂。
这里我用简单的暴力破解法来解决问题。当然处理过程中也做了一点小优化,即选择最短的字符串,用来划分子串。
通过该最短字符串的所有子串,在另外n-1个字符串中判断是否存在公共子串,若存在则判断是否为当前的最大长度。
整个思路实现起来比较简单,但是时间空间开销极大。
代码如下:
#include "stdio.h"
#include "string.h"
int strMatch(char *line,char *lineMin,int s,int e)
{
int length = strlen(line);
int i=0,j=0;
int flag =0;
int start =s;
int matchLength =0;
for(i=0;i<length;i++)
{
if(line[i]==lineMin[s])
{
start=s+1;
for(j=i+1;(j-i<=e-s)&&j<length;j++)
{
if(line[j]!=lineMin[start])
{
break;
}
start++;
}
}
if(start == e+1)
{
matchLength=e-s+1;
break;
}
}
return matchLength;
}
int main()
{
int testCase;
char line[20][256];
int i=0;
int min=256;
int max=0;
char *maxStr ;
int maxLength;
int length,minLength;
int j=0,count=0;
int m,n,k;
/* freopen("in2.txt","r",stdin);*/
maxStr =(char *)malloc(sizeof(char)*256);
scanf("%d",&testCase);
getchar();
while(testCase!=0)
{
for(i=0;i<testCase;i++)
{
gets(line[i]);
length = strlen(line[i]);
if(length<min)
{
j=i;
min = length;
}
}
max =0;
for(m=0;m<min;m++)
{
for(n=m;n<min;n++)
{
count =0;
for(k=0;k<testCase;k++)
{
if(k==j)
continue;
if((length=strMatch(line[k],line[j],m,n))!=0)
{
++count;
}
else
{
break;
}
if(count==testCase-1)
{
if(length>max)
{
max=length;
for(i=m;i<=n;i++)
{
maxStr[i-m]=line[j][i];
}
maxStr[i-m]='\0';
/*printf("%d : %s\n",length,maxStr);*/
}
}
}
}
}
if(strlen(maxStr)==0)
{
printf("\n");
}
else
{
/* length = strlen(maxStr);
for(i=0;i<length;i++)
{
printf("%2c",maxStr[i]);
}
putchar('\n');*/
printf("%s\n",maxStr);
}
scanf("%d",&testCase);
getchar();
}
/* fclose(stdin);*/
return 0;
}
以下为用时
我知道这肯定AC不了,还是需要通过后缀数组来解决,后面得好好学习学习。
验证结果为:
前三个为字符串,最后一个为公共子串。0代表退出,3代表输入的字符串数目。