KMP算法我的理解
KMP算法是一种串的模式匹配算法(O(m+n)),比一般字符串匹配算法(O(m*n))的优点(当匹配串内有大量类似字符---效率或对回溯有特殊要求的----类似IO操作时减少回溯)
在上面提及到的情况一般使用KMP算法可以提高很多的效率,而现在的实际使用情况貌似还是一般字符匹配也是有的..
下面介绍一下我个人对KMP算法的一点理解,大致分为三步
第一,理解对需要匹配串的预处理
第二,理解对预处理结果的使用即算法的实现
第三,了解一后进行匹配的具体方法
在我认为KMP其核心为预处理需要匹配的字符串得到一个next[] 方便匹配时使用,不多说上代码. ps.这种预处理的思想是不是可以用到其他方面呢? 待xiaoEight日后求证
1 void Get_next(char T[], int next[]) // 这里T[]是子串,而且T[ 0 ] 是放着字符串的长度 2 { 3 int i =1, j = 0; 4 next[1] = 0; 5 while (i < T[0]) ///// T[0] 为T字符串的长度 6 { 7 if (j == 0 || T[i] == T[j]) 8 { 9 ++i; ++j; 10 next[i] = j; 11 } // end if 12 else 13 { j = next[j]; } 14 } // end while 15 } // end Get_next()
每个对应位置的 next[j ]都对应一个k值 k为 k∈(1,k) 且为整数 满足 p[1]p[2]…p[k-1]= p[j-k+1]p[j-k+2]…p[j-1] 的集合中的max 即从字符串的第一个位置开始最大匹配(当前j位置j)往前相连的字串结束值+1 如下例:
(1 2 3 4 5 6 7) 1 2
(a b a a b c a) 中当 j=6 即当匹配到c时 就是判断b,ab,aab,baab..中能与前面相同的最长子串-----其中能匹配的为 a b 即可得知k-1=2 所以 k=3 next[6] = 3;
再了解k的意义之后在反观上面的 Get_next方法 其中 个人认为两个比较好的地方 一是当前面的串满足时,可直接在前面基础上继续判断;二则是当当前字符匹配不成功时(这里注意该方法将串的匹配有效的改变成为了单个字符的匹配也就是刚提到的第一个好的地方),j = next[j] 此处在我认为该处可能较为难以理解, 如下例
当预处理到第6个字符 'c' 时(得到的结果会放入next[7]中 此处next[x]中x与初始的i有关),由于前面已经说过可以直接利用前面的结果所以当此处时候不是从1开始检查匹配而是直接从3即(j = 3),在当该字符匹配不成功时(T[3] 为 'a' 而T[6]为 'c' ),去找当前不匹配字符(即T[3])的最大匹配----next[3].(该处有递归)
预处理完毕后的匹配方法
1 int Index_KMP(char S[], char T[], int next[]) //S是主串,T是匹配子串,next是预处理T[]得到的结果 2 { 3 int i = 1, j = 1; 4 while (i <= S[0] && j <= T[0]) // S[0].T[0]分别是主串和子串的长度 当超出长度时匹配完成 5 { 6 if (j == 0 || S[i] == T[j]) 7 { 8 ++i; 9 10 ++j; 11 } 12 13 else { j = next[ j ]; } 14 } /// end while 15 if (j > T[0]) //如果j > T[0] 说明在S找得到子串T 当j > T[0]说明最后一次匹配成功 j没有被重新赋值改变 16 { 17 return i - T[0]; //这时 i 是主串和子串匹配成功最后的的位置,还要减去子串的长度,才是子串在主串 匹配成功的起始位置 18 } 19 20 else return 0; 21 }
附普通匹配算法
1 int Index(char S[], char T[], int next[]) //S是主串,T是匹配子串,next是预处理T[]得到的结果 2 { 3 int i = 1, j = 1; 4 while (i <= S[0] && j <= T[0]) // S[0].T[0]分别是主串和子串的长度 当超出长度时匹配完成 5 { 6 if (S[i] == T[j]) 7 { 8 ++i; 9 10 ++j; 11 } 12 13 else { i = i - j +2 ; j = 1; } 14 } /// end while 15 if (j > T[0]) //如果j > T[0] 说明在S找得到子串T 当j > T[0]说明最后一次匹配成功 j没有被重新赋值改变 16 { 17 return i - T[0]; //这时 i 是主串和子串匹配成功最后的的位置,还要减去子串的长度,才是子串在主串 匹配成功的起始位置 18 } 19 20 else return 0; 21 }
有此大家都能可以看出不同之处为匹配过程中对不满足匹配时的 i 与 j 的处理,而且上述KMP仍可以继续优化还是不够效率高呢,看下次的学习咯咯