求两个串的第一个最长公共子串,如“shaohui”,“ahui”的最大子串为“hui”。
方法一:
1、将两个字符串中长度较长的那个字符串作为母串strOne,另一个作为子串strTwo。设置 数组mask[strOne.size()] = {0};用于记录在母串中找到的与子串strTwo匹配的公共子串的长度;maxlength = 0; end = 0(maxlength 记录当前找到的最长公共子串长度,end记录当前找到的最长公共子串在母串中的结束下标)
2、对于strTwo串的每一个字符strTwo[i],从右至左遍历母串strOne,如果str[j] != strTwo[i],mask[j] = 0;如果str[j] = strTwo[i],则mask[j] = mask[j-1]+1; 并判断mask[j]与maxlength的大小,若mask[j]大,将其赋值给maxlength,end = j;
s h a o h u i
初始 0 0 0 0 0 0 0
a 0 0 1 0 0 0 0
h 0 1 0 0 1 0 0
u 0 0 0 0 0 2 0
i 0 0 0 0 0 0 3
得maxlength = 3,j = 6。
代码如下:
//两个串中的第一个最长子串
string FindLongestCommonSubString(string strOne, string strTwo)
{
if ("" == strOne || strOne.length() <= 0 || "" == strTwo || strTwo.length() <= 0)
{
return NULL;
}
if (strOne.length() < strTwo.length()) //strOne始终是最长的, 相当于母串,strTwo相当于子串
{
string strTmp = strOne;
strOne = strTwo;
strTwo = strTmp;
}
int *mask = new int[strOne.length()];
int maxlength = 0;
int end = -1;
memset(mask, 0, sizeof(int)*strOne.length());
for (int i = 0; i < strTwo.length(); i++)
{
for (int j = strOne.length() - 1; j >= 0; j--)
{
if (strTwo[i] == strOne[j])
{
if (j == 0)
{
mask[j] = 1; //母串为第一个元素时需单独处理,按else中的处理会出现数组读取错误!
}
else
{
mask[j] = mask[j - 1] + 1;
}
}
else
{
mask[j] = 0;
}
if (mask[j] > maxlength)
{
maxlength = mask[j];
end = j;
}
}
for (int k = 0;k<strOne.length();k++)
{
cout<<mask[k]<<" ";
}
cout<<endl;
cout<<maxlength<<" "<<end<<endl; //当前最长公共子串长度及其在较长母串的终止下标
}
delete [] mask;
return strOne.substr(end - maxlength + 1, maxlength);
}
int main()
{
string strOne = "abractyeyt";
string strTwo = "dgdsaeactyey";
cout<<"第一个字符串: "<<strOne<<endl;
cout<<"第二个字符串: "<<strTwo<<endl;
cout<<"最长公共子串: "<<FindLongestCommonSubString(strOne, strTwo);
cout<<endl;
system("pause");
return 0;
}
方法二:若两个字符串为str1,str2.
将字符串str1和str2分别写在两把直尺上面(我依然用s1,s2来表示这两把直尺),然后将s1固定,s2的头部和s1的尾部对齐,然后逐渐移动直尺s2,比较重叠部分的字符串中的公共子串的长度,直到直尺s2移动到s1的头部。在这个过程中求得的最大长度就是s1、s2最大子串的长度。
下图是求解过程的图示(下图按照从下往上,从右至左的顺序看,即从最后一排的最后一个图往),蓝色部分表示重叠的字符串,红色的部分表示重叠部分相同的子串
其中s1="shaohui",s2="ahui",最后求得的结果为3
完整的代码如下:
int longest_common_substring(char *str1, char *str2)
{
int i,k,len1,len2,len,s1_start,s2_start,idx,curmax,max;
len1 = strlen(str1);
len2 = strlen(str2);
len = len1 + len2;
max = 0;
for(i = 0 ; i < len ; i++)
{
s1_start = s2_start = 0;
if(i < len1)
s1_start = len1 - i; //每次开始匹配的起始位置
else
s2_start = i - len1;
curmax = 0;
for(idx = 0 ; ( s1_start + idx < len1 ) && ( s2_start + idx < len2 ); idx++ )
{
if(str1[s1_start+idx]==str2[s2_start+idx])
curmax++;
else //只要有一个不相等,就说明相等的公共字符断了,不连续了,要保存curmax与max中的最大值,并将curmax重置为0
{
//max = curmax > max ? curmax : max;
if(curmax > max)
{
max = curmax;
k = s1_start+idx-1; //保存连续子串长度增加时连续子串最后一个字符在str1字符串中的下标位置,便于输出公共连续子串
}
curmax = 0;
}
}
//max = curmax > max ? curmax : max;
if(curmax > max)
{
max = curmax;
k = s1_start+idx-1;
}
}
//输出公共子串
char s[1000];
for(i=0;i<max;i++)
s[i]=str1[k-max+1+i]; //公共字串在str1中的下标起始位置为k-max+1,结束位置为k
s[i]='\0';
printf("最长公共子串为:");
puts(s);
return max;
}
int main(void)
{
char str1[1000],str2[1000];
printf("请输入第一个字符串:");
gets(str1);
printf("请输入第二个字符串:");
gets(str2);
int len = longest_common_substring(str1, str2);
printf("最长公共连续子串的长度为:%d\n",len);
system("pause");
return 0;
}