方法一(暴力法):利用三层循环进行逐个筛选,具体思想如下:从子串1的第一个字符开始依次与子串2的各个字符比较,相等则比较下一个字符,不相等时跳出最内层循环开始就本层循环中找出的最大公共子串长度与当前现有最大长度比对,进行更新最大公共长度以及起始位置,但是要知道这一大轮比下来你都是只让子串1的第一个字符作为开头依次与字符串2相比,这样只能求出最大公共子串的起始位置就为子串1的起始位置的情况,说简单点就是运气好,例如abcdkkk和baabcdadabc这两个字符串,你确实可以得到正确结果,但是若最大公共子串起始位置位于子串1的非第一个位置,例如abcdkkk和cbcdkifdao,你这求出来就是0,所以最外层循环就是起着让字符串1的每个字符与字符串2的每个字符都比较的功能,从这也可以看出为什么叫暴力法了,实在是太太太麻烦了,相当于一轮就要比较子串2的长度次,一共下来比较strlen(str1)*strlen(str2)次,时间复杂度是很高的。
#include<stdio.h>
#include<string.h>
int main()
{
int best = 0;
int start = 0;
char str1[30], str2[30];
int zuida(char *, char*,int &);
printf("请输入两个字符串:\n");
gets_s(str1);
gets_s(str2);
best = zuida(str1, str2,start);
printf("最大公共子串长度为:%d\n", best);
printf("最大公共子串在str1串中的起始位置为:%d\n", start);
return 0;
}
int zuida(char *p, char *q,int &s)
{
int len1, len2;//记录两串长
int i, j;
int k, n, max=0, long1,start=0;//k,n很重要,不能直接拿i和j进行比较,否则就达不到比较len1*len2的比较次数了;max记录最大公共串长,long1记录当前最大串长,start记录最大串长的起始位置
len1 = strlen(p);
len2 = strlen(q);
for (i = 0; i < len1; i++)
{
for (j = 0; j < len2; j++)
{
k = i;
n = j;
long1 = 0;
while (k < len1&&n < len2)//这层循环退出只有可能是长度超出范围或者出现不相同字符时
{
if (p[k] != q[n])
{
break;
}
k++;
n++;
long1++;
}
if (long1 > max)
{
max = long1;
start = i;
}
}
}
s = start;
return max;
}
方法二(动态规划法):大概是个这么个意思,有这么一个二维数组a[m][n],其中m和n分别为字符串str1和字符串str2的长度,规定a[i][j]为字符串str1以第i个字符为结尾同时字符串str2以第j个字符为结尾时它们拥有的最大公共子串长度,例如字符串str1为:abcdkkk 字符串str2为:baabcdadabc,那么a[2][4]=3,最大公共子串长度为3(abc),**但是a[3][6]可不等于4(它两必须要连续相同到最后一个字符才行,这里3和6位置上字符已经不相同了)**那么a[3][5]怎么求呢?答:比较str1[3]和str2[5],发现它两相同,那么我们是不是可以说a[3][5]的值就是a[3-1][5-1]的值加1,这里其实我觉得有点类似于kmp算法中求next数组的过程,也是通过前一个下标的最大公共前后缀以及当前比较的两个位置是否相等求出的,若当前两位置相等,那么next[j+1]=t+1(这里是按我自己想的表达的,可能会觉得说的有点莫名其妙),反之说简单点就是根据前面已有值推现在的未知值,当然若str1[3]不等于str2[5],则直接a[3][5]等于0,因为要求最大公共子串必须连续
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int best = 0;//接收最大公共子串长
char str1[30], str2[30];
int zuida(char *, char*);
printf("请输入两个字符串:\n");
gets_s(str1);
gets_s(str2);
best = zuida(str1, str2);
printf("最大公共子串长度为:%d\n", best);
return 0;
}
int zuida(char *p, char *q)
{
int i, j, max = 0;
int len1 = strlen(p);
int len2 = strlen(q);
//int shuzu[len1][len2];
int **shuzu = (int **)malloc(sizeof(int *)*len1);//动态分配一个二维数组
for (i = 0; i < len1; i++)
{
shuzu[i] = (int*)malloc(sizeof(int)*len2);
}
for (int i = 0; i < len1; i++)
{
if (p[i] == q[0])
{
shuzu[i][0] = 1;
}
else
{
shuzu[i][0] = 0;
}
}
for (int j = 0; j<len2; j++)
{
if (p[0] == q[j])
{
shuzu[0][j] = 1;
}
else
{
shuzu[0][j] = 0;
}
}
for (i = 1; i < len1; i++)
{
for (j = 1; j < len2; j++)
{
if (p[i] == q[j])
{
shuzu[i][j] = shuzu[i - 1][j - 1] + 1;
}
else
{
shuzu[i][j] = 0;
}
if (max < shuzu[i][j])
{
max = shuzu[i][j];//更新最大公共子串长
}
}
}
return max;
}
/*
22-26行是动态分配二维数组的过程,习题答案中给的是21行的方法,然而并没什么用,大概意思就是vs里面数组的
维数必须是一个字面常量,这就导致了在我事先不知道数组大小的时候无法定义一个静态数组,所以就只好用动态数组
定义的过程如下:
22行定义了数组的一维大小,也就是我们常说要把二维数组a[m][n]看成m个一维数组,而一维数组通常是以一个
int*的指针来保存一维数组的首地址来表示这个一维数组的,所以第一步我们先动态分配m个int*的指针数组
(int *shuzu[m]),此时一维定义好了,但是每个指针还没有指向一片内存,也就是说二维还没定义,所以呢,我们又要
整一个for循环为每个一维数组分配n个空间,这肯定就是我们最熟悉的动态一维数组的分配了
28-49行是先将数组第1行和第1列赋值了,这些值我们是知道的,但是为什么要事先弄这些呢?答:因为我们是通过前面推断后面,如果不这么做,请问那些一维或二维上是0的元素该怎么求(因为a[i][j]=a[i-1][j-1]+1)
*/