KMP
KMP是一种空间换时间的算法,特点是主串的指针不往回走,利用存储好的信息,避免重复的运算
要利用的信息存储在 部分匹配表(Partial Match Table)PMT:
char | a | b | a | b | a | b | c | a |
---|---|---|---|---|---|---|---|---|
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
value | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |
PMT
PMT中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。
例如:对于“aba”,它的前缀集合为{“a”,“ab”},后缀集合为{“ba”“a”}。
两个集合的交集为{“a”},那么长度最长的元素就是字符串"a"了,长度为1,所以对于"aba"而言,它在PMT表中对应的值就是1。
再比如,对于字符串"ababa",它的前缀集合为{“a”,“ab”,“aba”,“abab”},它的后缀集合为{“baba”,“aba”,“ba”,“a”},两个集合的交集为{“a”,“aba”},其中最长的元素为"aba",长度为3
PMT表怎么用
要在主字符串"ababababca"中查找模式字符串"abababca"。如果在j处字符串不匹配,那么由于前面所说的模式字符串PMT的性质,主字符串中i指针之前的PMT[j-1]位就一定与模式字符串的第0位至第PMT[j-1]位是相同的。
这是因为主字符串在i位失配,也就意味着主字符串从i-j到i这一段是与模式字符串的0到j这一段是完全相同的。而我们上面也解释了,模式字符串从0到j-1,在这个例子中就是"ababab",其前缀集合与后缀集合的交集的最长元素为"abab",长度为4。
所以就可以断言,主字符串中i指针之前的4位一定与模式字符串的第0位至第4位是相同的,即长度为4的后缀与前缀相同。这样一来,我们就可以将这些字符段的比较省略掉。
具体做法是,保持i指针不动,然后将j指针指向模式字符串的PMT[j-1]位即可。简而言之,以图中的例子来说,在i处失配,那么主字符串和模式字符串的前面6位就是相同的。又因为模式字符串的前6位,它的前4位后缀和后4位后缀是相同的,所以我们推知主字符串i之前的4位和模式字符串开头的4位是相同的。就是图中灰色部分。那么这部分就不用再比较了
有了上面的思路,我们就可以使用PMT加速字符串的查找了。我们看到如果实在j位失配,那么影响j指针回溯的位置其实是第j-1位的PMT值,所以为了编程的方便,我们不直接使用PMT数组,而是将PMT数组向后偏移一位。我们把新得到的这个数组称为next数组
根据next数组进行字符串匹配加速的字符串匹配程序。其中要注意的一个技巧是,在把PMT进行向右偏移时,第0位的值,我们将其设成了-1,这只是为了编程的方便,并没有其他的意义。next数组如下表所示。
char | a | b | a | b | a | b | c | a |
---|---|---|---|---|---|---|---|---|
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
pmt | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |
next | -1 | 0 | 0 | 1 | 2 | 3 | 4 | 0 |
def KMP(self,t