KMP算法——串的朴素模式匹配算法的优化、KMP算法的优化
什么是串的模式匹配
串的模式匹配:在主串中找到与模式串相同的字串,并返回其所在位置
例如:
主串:S='sdkjfklajsflkjlaskjf'
字串:'sdk'、'dkj'、'jfk'........
模式串:'as'、'flk'
朴素(简单)模式匹配算法
int Index(SString S,SString T){
int k=1;//指向主串起始位置
int i=1,j=1;//i指向主串起始位置,j指向模式串起始位置
while(i<=S.length&&j<=T.length){
if(S.ch[i]==T.ch[j]){
++i;
++j;
}else{//如果S.ch[i]!=T.ch[j],k向后移动
k++;
i=k;//i指向下一个位置
j=1;//j指向起始位置
}
}
if(j>t.length)
return k;
else
return 0;
}
性能分析
若模式串的长度为m,主串长度为n,
较好的情况:每个字串第一个字符就与模式串不匹配
匹配成功的最好时间复杂度:O(m)
匹配失败的时间复杂度:O(n-m+1)=O(n-m)约等于O(n)
最坏的情况:每个字串最后一个字符就与模式串不匹配,则直到匹配成功/失败最多需要(n-m+1)*m次比较
最坏时间复杂度为:O((n-m+1)*m)=O(nm)
朴素模式匹配算法的缺点:当某些子串与模式串能够部分匹配时,主串的扫描指针i经常回溯,导致时间开销增加。
KMP算法——串的朴素模式匹配算法的优化
由D.E.Knuth,J.H.Morris和V.R.Pratt提出,因此称为KMP算法。
设计思路:主串的指针不回溯,只有模式串的指针回溯。
/*KMP实现代码*/
Int Index_KMP(SString S,SString T,int next[]){//传入next数组
int i=1,j=1;
while(i<=S.length&&j<=T.length){
if(j==0||S.ch[i]==T.ch[j]){
++i;
++j;//继续比较后继字符
}
else j=next[j];//模式串向后移动
}
if(i>T.length)
return i-T.length;//匹配成功
else
return 0;
}
求next数组
串的前缀:包含第一个字符,且不包含最后一个字符的子串
串的后缀:包含最后一个字符,且不包含第一个字符的子串
当第j个字符匹配失败时,由前1~j-1个字符组成的串记为S,则:next[j]=S的最长相等前后缀长度+1(就是使S有最长前缀与最长的后缀相等,然后记下这个长度,并加1,就是next[j]的大小,即next[j]=最长重合的前后缀长度+1)
特别地,next[1]=0,(next[1]=0,next[2]=1)
/*求模式串T的next数组*/
void get_next(SString T,int next[]){
int i=1,j=0;
next[1]=0;
while(i<T.length){
if(j==0||T.ch[i]==T.ch[j]){
++i;
++j;
next[i]=j;//next[j+1]=next[j]+1
}
else j=next[j];//否则让j=next[j]循环继续
}
}
KMP算法性能分析
O(m)+O(n)=O(m+n)
O(m)是求next数组的时间复杂度
O(n)是KMP算法的时间复杂度
KMP算法优化——nextval数组
nextval数组就是对next数组的优化
/*nextval数组的求法*/
//先算出next数组,令nextval[1]=0
for(int j=2;j<T.length;j++){
if(T.ch[next[j]]==T.ch[j])
nextval[j]=nextval[next[j]];//从前往后找串中字符相等的字符,使后边数组元素等于前边的数组元素
else
nextval[j]=next[j];
}