朴素查找
int search1(const char *str,const char *childstr)
{
int i;
int len = strlen(childstr);
for(i = 0;i!='\0';i++)
{
if(strncmp(str+i,childstr,len)==0)
{
return i;
}
}
return -1;
}
int search(const char *str,const char *childstr){
int i,j;
int len = strlen(childstr);
for(i=0,j=0;str[i]!='\0'&&childstr[j]!='\0';){//n
if(str[i] == childstr[j]){//如果相等继续比较下一个
i++;
j++;
}else{//如果不相等,str回溯到 j 个字符串之前+1
i = i-j+1;//不相等的时候 主串回溯
j = 0;
}
}
if(childstr[j]=='\0')
return i-j;
return -1;
}
kmp算法
kmp算法最主要的是理解next数组的计算方法
首先声明在不同的地方对next数组的定义和计算有所不同,为了避免混淆,在此仅介绍一种。
next数组是对于模式字符串也就是需要被匹配字符串其中的每一个字母而言,为了在匹配时避免重复判断而存在,下面通过一个实例来介绍next数组的意义和具体用法。
首先摆出next数组
a 匹配串:b a b a b b;
0 0 1 2 3 1;
列如 s原串:b a b a b a b c b a b a b a b a b b;
a匹配串:b a b a b b;
先按暴力匹配可以得到如下
原串:b a b a b a bcb a b a b a b a b b;
匹配串 b a bab b;
在如图黑色字母处出现不匹配,如果继续暴力,则会将匹配串向后移一位,然后再重新将匹配串的指针指向首位重新开始逐个匹配。但现在思考一下,我已经匹配了过了前三个字符了,可不可以合理地利用已知的条件呢?
next数组就起到了运用已知条件的作用。如next[2]=1(从0开始),代表从匹配串队首开始向后走1(包括此点)与从a[2]开始向前走1的字符串完全相同。如next[3]=2,代表a[0],a[1]所组成的字符串与a[2]a[3]组成的完全相同。next[n]就代表n满足上述条件的长度。
这样的话next数组的求解就十分容易了,只需o(n)的复杂度;next[0]必为0,对于求解next[n],采用递推思想,如果next[n]=k;(k>0)则必须next[n-1]=k-1;如果next[n-1]=0;则看next[n]是否等于next[0]。如等于则next[n]=1。
接下来就是实现时next数组的现实作用了,如上图,设原串的指针为i,匹配串的指针为j。当匹配到a[3]失败时,要注意因为从a[j]即a[3]开始向前走next[j-1],即1与从队首开始向后走next[j-1]的字符是相同的,所以j回到next[j-1],即a[1]的位置,而i指针不变,再进行比较。这样就能高效地进行匹配。
结合图像来看,如右下图加黑的B与A部分就是匹配失败时next[j-1]的值代表B部分与A部分完全相同,所以直接跳过这部分,直接将j指针移到A部分的后一位处,即next[j-1]处。
//kmp O(n+m) 主串不回溯 调整j的位置
//当childstr[j] 和 主串不相等时 j退回到哪一个字符进行比较
void getNext(const char *pattern,int next[]){
next[0] = -1;
int len = strlen(pattern);
int j=0,k=-1;//next[j] pattern[0,j-1] 最大重复子串
while(j<len-1){
if(k==-1 || pattern[j] == pattern[k]){
//next[++j] = ++k;
//优化next数组
if(pattern[++j] == pattern[++k]){
next[j] = next[k];
}else{
next[j] = k;
}
}else{//前缀的最后一个字符和后缀最后一个字符不相等
k = next[k];
}
}
}
//str主串 pattern模式串
int kmp(const char *str,const char *pattern){
int i=0,j=0;
int next[strlen(pattern)];
getNext(pattern,next);
while(str[i]!='\0'&&pattern[j]!='\0'){
//j==-1 第一个字符就不相等 只能改变j不能改变i
if(j==-1||str[i]==pattern[j]){
++i;
++j;
}else{
j = next[j];//j回退到指定的位置继续比较
}
}
}