我們要找到一個短字符串(模式串)在另一個長字符串(原始串)中的起始位置,也就是模式匹配,最關鍵的是找到next數組。最簡單的算法就是用雙層循環來解決,但是這種算法效率低,kmp算法是針對模式串自身的特點,當失配時,能夠利用next數組得到的信息直接跳過不可能匹配成功的位置字符。例如模式字符串“ababaaaba”,假設當匹配到第6個字符“a”發生錯誤,傳統方法是原始字符串往后移動一個,但是原始串顯然第2個字符是b(因為之前匹配過了),不可能是模式串的起始字符,而next會發現從原始串的第1個字符開始的“aba”和第3個字符開始的“aba”是一樣的,所以直接將模式串的第1個字符和原始串的往后移動2個的字符比較,而此時直接用模式串第4個字符與之前原始串中失配的字符比也就是說模式串中第6個位置和第4個位置的字符位置等價,你失配了我來,因為他們前面有相同的串,而且其中一個是從模式串的起始位置開始的,這也就是next數組的定義。
說的這么詳細是因為考研期間這個部分非常難,我本人也是用了幾天才參透這里面的原理,畢竟是非常著名的算法,不是背上來代碼那么簡單。
字符串一般是從編號1開始,第0個位置存放長度,簡單起見下面的代碼簡化表示。
#define _CRT_SECURE_NO_DEPRECATE//vs編譯器編譯c語言需要加此條語句 #include <stdio.h> #include <stdlib.h> #include <string.h> void get_next(int *next, char *Array,int len) /*求NEXT[]的值*/ { int i = 1, j = 0; //如果字符串不是從編號1開始,則初始化i=0,j=-1,i<len-1 next[1] = 0;//初始化第一個字符的next值為0 while (i < len)//整個過程中i變量一遍走過,而j變量可能會回溯,i一直在j后面 { if (j == 0 || Array[i] ==Array[j])//起始或者字符有重復,那么下一個位置i++和j++的位置等價, { i++; j++; if (Array[i] == Array[j]) next[i] = next[j]; //優化找到最開始的等價位,等價位的等價位 else next[i] = j; } else j = next[j]; //回溯,正是利用了next數組本身的回溯的功能 } } int main() { char A[] = "0ababaaaba";//為了使得字符數據從編號1開始 int next[sizeof(A)-1] = { 0 };//初始化 int len = sizeof(A)-1; get_next(next, A,len); for (int i = 1; i < len;i++) printf("%d ",next[i]); system("pause");//vs運行需要加此條代碼 return 0; }
運行結果:若為未改進的算法,模式串“ababaaaba”的next運行結果為
0 1 1 2 3 4 2 2 3,
改進算法運行結果為0 1 0 1 0 4 2 1 0。
考試題也有可能不考代碼,我的經驗是先根據算法寫出為改進的,然后從頭開始對應的字符一樣就改為前面那個字符的next值,以此類推。