strstr函数就是一个在主串中寻找是否含有某个子串的
返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针。
匹配过程不包括终止空字符,但它就此停止
我们来看一下思路吧
接下来看代码:
char* my_strstr(const char* str1, const char* str2)//加const为了不让值改变
{
assert(str1 && str2);
const char* s1 = str1;
const char* s2 = str2;
const char* cur = str1;
while (*cur)
{
s1 = cur;
s2 = str2;
while (*s1&&*s2&&(*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
return (char*)cur;
cur++;
}
return NULL;
}
这个算法实际上就是BF算法(BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。)
那我们该怎么优化呢?
于是就有了
KMP算法(KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n))
K算法的核心就是next数组,那么next数组该怎么理解呢?
我们先来看看KPM算法执行流程吧!
KPM相对于BF算法来说,他并不是完全回溯到子串的第一个元素,而是记录刚刚子串和主串相同部分的区域,在这区域有相同的部分,那直接用子串的第一部分移动到主串的第二部分,然后开开始向后找,这样说可能不太理解,那我用图解来解释一下!!
当然,在有的情况根本就找不到相同的部分啊,那我们该怎么搞呢?
其实这里得引用一个next数组,用next数组来确定回溯的位置,那么next的原理是什么呢?
我们刚刚说了在匹配的字符串中,找到相同的部分,然后就可以直接唯一这个字符串
我们现在就得研究这个next该怎么写出来!
我们看图:
规定第一个元素为-1,第二个为0,然后在第三个开始确定重叠长度,但是每次都不算自己本身的,就这样一个一个找,我们看最后一个元素A,它前面重叠的长度有3个,这些值我们都得传到next数组里,但是我们怎么来确定next的所有值呢?
我们来推导看看
假设第一部分重叠最后一个元素下标为k-1,第二部分的下标那就是i-1了(i是要确定的那个数组元素下标)
因为第一部分和第二部分长度相同,那么就是next[0]…next[k-1]==next[i-k]…next[i-1]
如果next[k]==next[i],那么next[0]…next[k-1]next[k]==next[i-k]…next[i-1]next[i],也就是是长度在原来基础上+1,那么next[i+1]=k+1
但是如果不相等呢?
那么k就得回溯,直到next[k]==next[i],这有进入上一个循环,但是我们怎么设计这个k的回溯呢???这是一个重点,也是一个难点!!
我们用k=next[k]来回溯,为什么呢?
我用图解一下
假设next[15]=7
如果next[15]==next[7],那么next[16]=7+1=8
如果不相等呢??那我们就得找到一个k值让next[k]==next[i-1]
我们假设next[7]=3
因为5=6,1=2,3=4所以1=2=3=4 因为这个next[15]!=next[7],现在k回溯到next[k]的位置,如果相等了就可以继续循环了 ,如果还不等,那在依次重复上面的操作,这有点递归那个味了,哈哈,现在知道为什么k=next[k]了嘛,其实这个next[k]不就是找上一个重复部分的位置的嘛,上面那个图,看找到了3这个位置,如果next[15]= =next[3]了的话,那next[15]= =3+1=4,如果不想相等就继续,但是当k=next[k]一直找到第一个元素了,我们规定了next[0]=-1,那么如果一直找到第一个元素了,那么next[i]=0了;所以我们写代码的时候在判断next[i-1] = = next[k]的时候得改成k==-1||next[i-1]==next[k]
那么我现在附上代码:
void Getnext(char* str2, int* next,int lenstr2)
{
next[0] = -1;
next[1] = 0;
int i = 2;
int k = 0;
while (i < lenstr2)
{
if (k == -1 || (next[i - 1] == next[k]))
{
next[i] = k + 1;
i++;
k++;
}
else
k = next[k];//回溯
}
}
int KMP(char* str1, char* str2,int pos)
{
assert(str1 && str2);
int lenstr1 = strlen(str1);
int lenstr2 = strlen(str2);
if (lenstr1 == 0 || lenstr2 == 0) return -1;
if (pos<0 || pos>lenstr1) return -1;
int i = pos;
int j = 0;
int* next=(int*)malloc(sizeof(int) * lenstr2);
assert(next != NULL);
Getnext(str2, next,lenstr2);
while (i < lenstr1&&j<lenstr2)
{
if (j==-1||str1[i] == str2[j])
{
i++;
j++;
}
else
j = next[j]; //平移子串,从新判断
if (j >= lenstr2)
return i - j;
}
return -1;
}
int main()
{
char arr1[20] = "aabbccddbbabadba";
char arr2[3] = "cd";
int str = KMP(arr1, arr2,0);
printf("%d", str);
return 0;
}