例一:
假如我们要在ABCAABCAB中搜索ABCAB字符串
第1次比较,我们从第一个字母开始比较,直到比较到红色标记的地方才不相等。
A B C A A B C A B
A B C A B
按我们正常思维,我们会把字符串ABCAB后移一位,从头继续和ABCAABCAB进行比较,如下所示
第2次比较
A B C A A B C A B
A B C A B
第3次比较
A B C A A B C A B
A B C A B
第4次比较
A B C A A B C A B
A B C A B
第5次比较
A B C A A B C A B
A B C A B
在第5次比较时,我们在源字符串中,找到了我们需要查找的字符串。
总结:
在第2次和第3次的比较中,我们发现这2次比较是无意义的,因为我们知道在我们不匹配的位置前一个字符是A,移动第一次时,是拿C和A(不匹配的位置)比较,第二次移动时,是拿B和它比较,直到第3次移动时,才是A和A比较。所以理想情况应该是,在第一次比较不满足时,我们就直接后移动3位,进行第4次的比较。
在第一次比较到不匹配的位置时,我们希望把第1个A直接移动到第4个A的位置(移动3位);继续观察ABCA字符串(因为最后一个B已经不匹配了),我们发现A和A之间间距3,刚好是已匹配字符串长度4减去A占的位数1。
例二:
我们在换一种情况,假如我们要在ABBABABCABC中搜索字符串ABBABC
ABBABABCABC
ABBABC
我们发现最终比较到C时,和源字符串不相等。根据情况一的经验,我们观察前一位字符是B,但是在ABBAB中,前面还有2个B,此时我们应该把第1个B移动到位置5,还是把第2个B移动到位置5呢?还是要做2次移动,比较2次呢?我们在次仔细观察字符串ABBAB,我们发现在ABBAB中,最后2个字符是AB,前面2个也是AB,只有中间一个B,所以理想情况是,我们直接把开头的AB,移动到最后AB所在位置:
ABBABABCABC
ABBABC
如上所示,也就是后移3位。移动位数刚好也等于已匹配字符串长度5减去AB字符串长度2。
观察情况一和情况二后总结
每次移动,都是在出现不匹配字符时;而每次确定需要移动多少位时,都是根据不匹配字符前面的子串来决定的。我们以例二中的ABBABC为例,假如不匹配的位置出现在ABBABC红色的B时,我们观察ABBA前后共有长度是1(只有A),所以需要后移3位(4减去1)。所以我们依次计算出子串共有长度如下:
A 0
AB 0
ABB 0
ABBA 1
ABBAB 2
ABBABC 0
其实第1个和最后一个共有长度,我们用不到。当比较第1个字符就不匹配时,根本无需计算共有长度,我们自然后移一位。当最后一个字符不匹配时,我们计算出ABBAB的共有长度为2,需要后移5-2位,即3位。
所以在不匹配时,后移位数公式如下:
已匹配子串长度 - 已匹配子串共有长度
既然知道了公式需要用到共有长度,下面我们就说下共有长度如何计算。
A 前缀后缀都为空,共有长度0
AB 前缀[A]后缀[B],共有长度0
ABB 前缀[A,AB]后缀[BB,B],共有长度0
ABBA 前缀[A,AB,ABB]后缀[BBA,BA,A]共有长度(A)1
ABBAB 前缀[A,AB,ABB,ABBA]后缀[BBAB,BAB,AB,B]共有长度(AB)2
ABBABC 前缀[A,AB,ABB,ABBA,ABBAB]后缀[BBABC,BABC,ABC,BC,C]共有长度0
所以
ABBABC字符串
每一位的共有长度为如下
A B B A B C
0 0 0 1 2 0