您的位置:首页 > 其它

KMP算法查找相同字符串

2017-09-13 21:46 176 查看

问题:

现在有两个字符串A和B,问你在A中是否有B,有几个??
        其实刚开始遇到这个问题的时候,我觉得挺简单的呀!依次循环过去查找不就可以了,当然这样做肯定能实现,而且程序编写简单粗暴,两个循环就解决了。但是这种方法的时间复杂度是O(M*N),M是字符串A的大小,N是字符串B的大小,这样时间复杂度就太高了,我们需要一个时间复杂度低的算法,这样KMP算法就孕育而生了,算法的时间复杂度为O(M+N)。

KMP算法基本原理

算法的第一步就是建立一个next数组,这个数组有啥用,这个后面解释。next数组表示的是字符串B的前缀和后缀最大相等的数量,这句话什么意思??举个例子吧。
假设A=“abaabaabbabaaabaabbabaab”
B="abaabbabaab"
        对于前i个数,假设i=4,这字符串“abaa”的前缀是"a",“ab”,"aba";后缀是"a","aa","baa",其中前缀和后缀相同的字符串有一组,是“a”,所以最大相同的字符串字符数为1,故next[i-1=3]=1(数组是从0开始计数,所以这里是i-1);根据这个规律我们可以写出数组B的next={0,0,1,1,2,0,1,2,3,4,5};        算法的第二步就是利用字符串B对字符串A逐步右移,在A中移动的位置用j来表示,B中移动的位置用k来表示,以上面例子来解释这个算法,当j=0,k=0时,A字符串的‘a’和B字符串的'a'两个字符相等,则j++;k++;j=1,k=1时,A字符串的‘b’和B字符串的'b'两个字符相等,继续j++,k++;依次类推到i=5,j=5时,A字符串的‘a’和B字符串的'b'两个字符不相等。这时候j=next[j-1]=2,继续将A数组的第i个字符串与B数组的第j个字符串相比较,此时A字符串的i=5为‘a’,B字符串j=2(此时j的值已经发生了改变)的值为‘a’,两个字符串相同,则i++,j++;这时候A字符串的数组为'a',B字符串的数组为‘a’,重复上述过程。最后知道i的值与A字符串长度相等的时候循环结束。这里需要注意的一点是,当比较字符与模板字符一直不相等,就比如假设上述例子i=5时遇到的字符不是'a',而是‘c’,则j=2的字符也不相等,则j=next[j-1]=0;当j=0时的字符也和‘c’不相等,如果遇到这种情况不处理的话就会出现溢出的情况,所以我们需要对其进行处理。当我们j=0时还是与第i个字符不相等时,我们就应该跳过这个字符串,即i++;在编程的时候这一点尤其要注意。        介绍完该算法的整个过程,现在来分析一下它的可行性,以及为什么用产生一个next数组??这个数组有什么作用??如果你不考虑算法的层面,仅仅从数学层面上来做这道题目,我们肯定第1个字母进行比较,然后在第6个字符的时候断开了,然后你会立即从第4个字符开始进行比较,你这样做的原理是字符串A第6个字符前面两个字符与字符串B的前两个字符相同,所以我们选择第4个字符开始,这时候你再想想next数组产生的原理,有没有发现点什么??
        我们在第6个字符的地方断的,说明A和B前5个字符相同,next[5-1](表示第5个数)表示前缀和后缀最大相同的字符串数目,这时候是2。这说明B字符串的第1和第2的字符肯定和A数组中第4和第5个字符相等,如果这样的话我们只需要直接B数组的第三个元素与A数组的第6个元素比较就可以了。现在知道next数组的作用了吧!!!
下面是我对该算法写的c代码,仅仅参考,有错误请指教:
#include"stdio.h"
#include"string.h"
#include"malloc.h"
#define N 20			//定义字符串模板长度
#define N1 50			//定义需要匹配的字符串的长度
int *get_array(char *B);	//得到next数组
void main()
{
char B
="aba";		//定义模板字符串
char A[N1]="abaabaabbabaaabaabbabaab";		//定义需要匹配的字符串
int *next;		//定义next数组
int i=0,j=0,flag=0;
next=(int *)malloc(strlen(B)*sizeof(int));		//给next数组开辟空间
next=get_array(B);			//得到next数组的值
while(i0)				//防止一个字符一直找不到匹配导致溢出
{
j=next[j-1];
}
else				//如果有一个字符一直找不到匹配,则进行下一个字符重新匹配
{
i++;
j=0;
}

}
}
if(flag)			//判断是否存在该模板字符串
{
printf("exist the number and exist number is %d ",flag);
}
else
{
printf("not exist the number");
}
getchar();

}
int *get_array(char *B)
{
int L=strlen(B);		//L代表模板字符串的长度
int key=0;				//用来记录前缀和后缀相同的数目
int *next;				//找到前缀和后缀相同的数目最大值
next=(int *)malloc(L*sizeof(int));
for(int i=0;i<strlen(B);i++)
{
next[i]=0;			//给next数组赋初值
}
for(int i=0;i<L;i++)		//查找前i个字符前缀和后缀的数目
{
int max=0;			//每次对其归0
for(int j=0;j<i;j++)		//找出前i个字符的前缀和后缀
{
int flag=0;				//归0复位
for(int k=0;k<=j;k++)	//对前i个字符的前缀和后缀依次比较进行匹配
{
if(B[k]!=B[i-j+k])	//如果前缀和后缀不相同
{
flag=1;			//标志位,表明该前缀和后缀不相同
break;
}
}
if(flag==0)			//如果前缀和后缀相同
{
key=j+1;		//记录下前缀的数目
if(key>max)		//判断是否最大
{
max=key;
}
}
next[i]=max;		//将最大数目的前缀赋值给next数组
}
}
return next;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: