研究了7-8小时的KMP算法,资料倒是看了不少,可惜还是没有得其精要。。。
好资料:
http://blog.csdn.net/v_july_v/article/details/7041827
http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/
http://www.cnblogs.com/10jschen/archive/2012/08/21/2648451.html
目前真正领会到的精髓如下:
1. 根据匹配字符串的本身特征来加速匹配过程;
2. 这种特征类似于某种对称。
在匹配字符串中找出这种对称后, 如果在实际匹配过程中,这种对称被包含在已经匹配的子字符串中,那么就可以利用这种对称关系。也就是说,已经匹配的子字符串中,如果前缀和后缀含有一致的子子字符串,那么因为子字符串同主字符串已经匹配,所以后缀必定存在于已经匹配的主字符串中。也所以,前缀也必定存在于已经匹配的主字符串中。所以,可以在不移动主串指针的情况下,直接将匹配字符串向后移动n位,和相同的子串匹配。这样就避免了主串指针的回溯。
代码实现如下:(目前已经理解KMP算法的逻辑,但自己写的代码比他人的不是一个级别的,惭愧!)
1. KMP主体:
int KmpSearch(char* s, char* p) { int i = 0; int j = 0; int sLen = strlen(s); int pLen = strlen(p); while (i < sLen && j < pLen) { //①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++ if (j == -1 || s[i] == p[j]) { i++; j++; } else { //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j] //next[j]即为j所对应的next值 j = next[j]; } } if (j == pLen) return i - j; else return -1; }
2. next数组:
void GetNext(char* p,int next[]) { int pLen = strlen(p); next[0] = -1; int k = -1; int j = 0; while (j < pLen - 1) { //p[k]表示前缀,p[j]表示后缀 if (k == -1 || p[j] == p[k]) { ++k; ++j; next[j] = k; } else { k = next[k]; } } } //优化过后的next 数组求法 void GetNextval(char* p, int next[]) { int pLen = strlen(p); next[0] = -1; int k = -1; int j = 0; while (j < pLen - 1) { //p[k]表示前缀,p[j]表示后缀 if (k == -1 || p[j] == p[k]) { ++j; ++k; //较之前next数组求法,改动在下面4行 if (p[j] != p[k]) next[j] = k; //之前只有这一行 else //因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]] next[j] = next[k]; } else { k = next[k]; } } }