第四章小结:
一、串
1、BF算法
将模式串跟主串从开头一个一个比较,如果匹配失败,又从模式串第二个字符一次比较,匹配,++i;++j;不匹配,i=i-j+2;j=1;
一般情况下,BF算法时间复杂度为O(m*n),数据量不大时候,执行时间近似为O(m+n),但如果是庞大数据,则有可能会运行超时。
BF算法浅显易懂,但是对于大型数据处理来说实用性不高。
2、KMP算法
KMP算法是基于我们对BF算法的不满而设计出来的(我个人是这样认为的),算法的改进重点放在如何减少不必要的比较上。
匹配失败:
S: aaaaabchu
T: aaab
BF算法:
S:aaaaabchu
T: aaab
改进型算法:
S:aaaaabchu
T: aaab
可以看出,改进型算法省去了BF算法的2次比较(在上面的情况下)。
我们的改进有什么依据吗?
当失配时,可能在已比较的模式串中会有与主串当前位置字符相匹配的字符,而且每一个字符都有可能(这是在没有考虑其他字符是否匹配的情况下)。虽然每一个字符都有可能,但是其它字符也有一些确定的限制,那么,我 们就必须先考虑确定的限制。在满足确定性限制的情况下的可能性,才是真正有意义的。为了去除没有意义的,没有必要的比较,我们的限制是:模式串当前将要比较字符的前面的所有字符都必须匹配主串对应的字符,同时为了不遗漏,我们必须要确保这个相匹配的字符串是最长的。即是将模式串向后移动的过程中第一个完全匹配的位置。当我们拿一个字符串来和模式串进行匹配时,在失配的情况下,我们只需要拿满足以上限制条件所对应位置的字符来进行比较便可以了。因为上述条件下对应的字符便是在已有信息的基础上去除没有意义的比较后可能与主串匹配的字符。这个过程便是KMP算法的原理和思想。
总结上述分析,我们可以确定KMP算法的控制流程:
1.模式串与主串逐一比较
2.如果失配,模式串指针回溯到满足限制条件的地方,重新比较,而不是如同BF算法一样,主串前移一位从头比较
3.重复上述过程,直至匹配完成或失败
回溯到满足限制条件的地方需要用到函数next(即主串前移的步数)。那么这个next的代码该如何写呢?
next数组便是前缀中的最长相同前后缀,究竟什么意思呢,下面就来举几个例子:
对于abcdabd这个数组:
abcdabd 橙色的两组即为最长的前后缀
对于abccba这个数组:
abccba 橙色的两组即为最长前后缀
具体代码:
void next1(string t){ int i=0,j=0; next[0]=-1; while(i<tlen-1){ if(j==-1||t[i]==t[j]){ ++i; ++j; next[i]=j; } else{ j=next[j]; } } }
KMP代码:
int KMP(string s,string t){ int i=0; int j=0; next[0]=-1; while(i<slen&&j<tlen){ if(j==-1||s[i]==t[j]){ ++i; ++j; } else{ j=next[j]; } } if(j==tlen){ return i-j+1; } else{ return 0; } }