KMP算法,你想知道的都在這裏!(算法理解)

簡潔

我相信不少人都據說過KMP算法(PS:在上數據結構的時候,這個算法自始至終都沒想明白)java

你們也知道KMP算法是用來尋找目標子串的算法,可是都沒有真正搞懂KMP。以前,我也是如此,我疑惑的有:程序員

  1. Next數組中的值是如何定下來的?
  2. 獲得Next數組之後,又是如何遍歷的?

但願這篇文章能對大家有所幫助!算法

視頻推薦

KMP這種算法,單單靠看文章,仍是很難弄懂的,結合圖像,以及視頻的幫助會更容易弄懂。這裏我推薦一個視頻:KMP算法數組

我的也是經過這個視頻,恍然大悟的。這裏仍是很是感謝這位做者的分享。數據結構

我的總結

經過上述視頻,我想你們應該都有一個大概的瞭解了。該算法我將它劃分爲兩個部分:ide

  1. 求Next數組
  2. 匹配字符串

我將分這兩個部分進行細緻講解函數

第一部分

求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數組的重要性。

相關文章
相關標籤/搜索