一、暴力求解法(朴素算法)
实现代码:
int BF(char *str,char *sub,int pos)//暴力求解在一个字符串中查找字串出现的首次位置
{
int lenstr = strlen(str);
int lensub = strlen(sub);
int i = pos;//主串
int j = 0;
while(i < lenstr && j < lensub)
{
if(str[i] == sub[j])
{
i++;
j++;
}
else
{
i = i-j+1;//两个不匹配时,i退回上一次的下一个位置(此时i相对上一个位置已经走了j步)
j = 0; //j退回最开始
}
}
if(j >= lensub)//此时代表找到
{
return i-j;//i-j代表字符串首次出现的位置
}
return -1;//没有找到
}
二、KMP算法
KMP 算法:相对于 BF 算法来说 KMP 算法更为高效,原因在于 BF 算法的时间复杂度是:O(mn) m 代表主串的长度,n 代表子串的长度。而 KMP 的话,时间复杂度就变为 O(m+n);接下来我们看一下,具体的实现 过程,还是一样,我们举例来看:KMP 和 BF 唯一不一样的地方在,我主串的 i 并不会回退,并且 j 也不会移动到 0 号位置。
(1)next数组:也就是用 next[j] = k;来表示,不同的 j 来对应一个 k值,
这个 k 就是你将来要移动的 j 要移动到的位置。
next[i]表示除去第i个数,字符串里从第一个数到第i-1个字符串
前缀和后缀最长重复的个数。
nextval数组:从第二位开始,若要求nextval[i],将next[i]的值与对应位的值进行比较(如:i的值为‘b’,next[i]=3,则将i的值与第3位的值进行比较),若相等nextval[i]=nextval[next[i]],若不相等,nextval[i]=next[i];
(2)k值的求解:
1>:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 开始,另一个以 j-1 下标结尾。
2>不管什么数据 next[0] = -1;next[1] = 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始; -1 的理由:当主串为–”defrdes” 子串为:”abc” 一开始就匹配失败。 0 的理由:当子串在 1 号下标匹配,此时为 0;
next数组的代码实现:
void GetNext(int*next,char *sub)//KMP的next数组
{
next[0] = -1;
next[1] = 0;
int j = 2;
int k = 0;
int len = strlen(sub);//字串的长度
while(j <= len)
{
if(k == -1 || sub[k] == sub[j-1])//Pk==Pj的情况
{
next[j] = k+1;
j++;
k = k+1;
}
else
{
k = next[k];//找不到最长的可以往前退,找长度较小的前缀后缀串
}
}
}
KMP算法的代码实现:
int KMP(char *str,char *sub,int pos)//KMP算法,从pos位置开始查找
{
int i = pos;
int j = 0;
int lens = strlen(str);//主串的长度
int lensub = strlen(sub);//子串的长度
int *next=(int *)malloc(lensub*sizeof(int));
assert(next!=NULL);
GetNext(next,sub);//调用GetNext函数得到next数组
while(i<lens&&j<lensub)
{
if(j==-1||str[i]==sub[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j>=lensub)
{
return i-j;
}
return -1;
三、统计子串在主串中出现的次数
int KmpCount(char *str,char *sub,int pos)//统计子串在主串中出现了多少次
{
int lens = strlen(str);
int lensub = strlen(sub);
int i = pos;
int j = 0;
int count = 0;//记录出现的次数
int *next = (int *)malloc(sizeof(int) *(lensub+1));
GetNext(next,sub);
while(i < lens)
{
//如果匹配到此时j处于next[j]的下一个位置,
//回归到next[j]
if(j>=lensub)//匹配到一个
{
count++;
j = next[j];//j回到next[j]的位置
}
if(str[i] == sub[j]||(j==-1))
{
i++;
j++;
}
else
{
j = next[j];
}
}