我相信不少人都據說過KMP算法(PS:在上數據結構的時候,這個算法自始至終都沒想明白)java
你們也知道KMP算法是用來尋找目標子串的算法,可是都沒有真正搞懂KMP。以前,我也是如此,我疑惑的有:程序員
但願這篇文章能對大家有所幫助!算法
KMP這種算法,單單靠看文章,仍是很難弄懂的,結合圖像,以及視頻的幫助會更容易弄懂。這裏我推薦一個視頻:KMP算法數組
我的也是經過這個視頻,恍然大悟的。這裏仍是很是感謝這位做者的分享。數據結構
經過上述視頻,我想你們應該都有一個大概的瞭解了。該算法我將它劃分爲兩個部分:ide
我將分這兩個部分進行細緻講解函數
求Next數組是KMP算法的重點,也是難點!想搞懂這部分必定要看視頻!必定要看視頻!必定要看視頻!我以爲上述的視頻,將這部分說的很清楚了。這裏作一個總結:
(視頻裏將目標子串的第i
號位置映射到Next指針的第i+1
號位置。我這裏根據程序員的習慣,將目標子串的第i
號位置映射到Next指針的第i
號位置)優化
我將Next[i]作一下定義:指針
Next[i]表示:code
- 若是(substr.charAt(i) != str.charAt(j)),這時候,將i = Next[i],再進行字符串匹配。
這種表達雖然不嚴謹,可是我感受比較直觀hhhh。也即:若是目標子串第i個的值與匹配串第j的值不一致的時候,將目標子串向右滑動,滑動到i = Next[i]。
Next[i]值的定義:
Next[i]的值爲:
- 第0位爲頭的前綴s1,與第i-1爲尾的後綴s2,尋找他們的最長相同先後綴。
- 最長相同先後綴:就是s1與s2的值是相同的,可是s1和s2的長度都應小於i-1.
上面這兩個定義值得細品,這也是KMP的精髓,也正是Next數組,KMP算法才能更加高效!
其中須要注意的點是:Next[0] = 0,Next[1] = 0,由於從第2個開始,s1和s2纔有意義。
看完上述定義,我將求Next數組的函數實現出來。
注:該函數求Next數組時間複雜度較高,便於理解,可是實用性不高。優化策略做者採用DP,請看後一篇博客。
/** * 該函數是爲了,根據目標子串subStr,求解該子串的Next數 * @param 目標子串substr * @return subStr的Next數組 */ static int[] CalculateNext(String substr){ //init int[] Next = new int[substr.length()]; //求解Next[i] for(int i = 2; i < substr.length(); i++){ //第0位爲頭的l前綴,與第i-1爲尾的r後綴 int left = 0; int right = i - 1; String l = Character.toString(substr.charAt(left)); String r = Character.toString(substr.charAt(right)); int maxLen = 0; //當l與r均小於i-1的時候,擴大搜索最長相同先後綴 while(left < i - 1){ //若是兩個字符串相同,說明這是 目前 最長的相同先後綴 if(l.equals(r)) maxLen = l.length(); left++; right--; //繼續擴大搜索範圍 l = l + Character.toString(substr.charAt(left)); r = Character.toString(substr.charAt(right)) + r; } //最終的maxLen即爲Next[i]的值 Next[i] = maxLen; } return Next; }
這個部分就簡單不少了,只要注意一點小細節,總體思路很是清晰直觀:
在保證目標子串與主串匹配的過程當中,不會越界的狀況下:進行目標子串的滑動操做
當subStr.charAt(i) == str.charAt(j)
的時候,i與j同時往右移動便可,當徹底匹配的時候,就返回
當subStr.charAt(i) != str.charAt(j)
的時候,i = Next[i]便可。
這裏j須要注意一個細節,若是i == 0,須要j往右移一位,由於和主串的第一位都不匹配,前面就再也沒有能匹配的串了。
/** * 該函數用於查看目標子串在主串中第一次出現的位置 * @param 目標子串subStr * @param 主串str * @param Next數組 * @return str中,第一次出現subStr的位置 */ static int findPosition(String subStr,String str,int[] Next){ //初始化兩個字符串的指針 int i = 0;//i爲目標子串的指針 int j = 0;//j爲主串的指針 //保證目標子串與主串匹配的過程當中,不會越界的狀況下: while(j + subStr.length() - i <= str.length()){ //當兩者匹配的時候,i與j同時往右移 if(subStr.charAt(i) == str.charAt(j)){ i++;j++; //當徹底匹配的時候,返回 if(i == subStr.length()) return j - subStr.length(); } //不匹配的時候,主串指針j只有在i = 0的時候,才右移動。 else { if(i == 0) j++; //i值都須要更新 i = Next[i]; } } return -1; }
這裏主要探討的是Next數組,這個是KMP算法的核心!之前考試都是考Next數組的求解,可見Next數組的重要性。