KMP算法----分析---Java代碼----字符串搜索算法

KMP算法

KMP算法----分析---Java代碼----字符串搜索算法

1、題目介紹

     * KMP算法

     * Date:2018-12-25 22:00

     * 題目:給定兩個字符串str和match,長度分別爲N和M。實現一個算法,

     * 如果字符串str中含有含有子串match,則返回match在str中的開始位置,

     * 不含有則返回-1。

 

2、思路分析

  * <p>

     * 普通解法:時間複雜度較高。從每個字符出發,匹配的代價都可能是O(M),

     * 一共N個字符,所以,整體的時間複雜度爲O(MN)。

     * 原因分析:普通解法的時間複雜度之所以這麼高,是因爲每次遍歷到一個字符時,

     * 檢查工作相當於從無開始,之前的遍歷檢查不能優化當前的遍歷檢查。

     * <p>

     * KMP算法

     * 時間複雜度:O(M+N)

     * 空間複雜度:O(M)

     * KMP算法原理:首先構建一個大小等於match長度的數組nextArr,

     * 這個數組元素的含義:nextArr[i]表示在match[i]之前的字符串match[0...i-1]中,

     * 前綴子串與後綴子串最大匹配長度;

     * 其中前綴子串必須以match[0]開頭最長以match[i-2]結尾,

     * 後綴子串必須以match[i-1]結尾最長以match[1]開頭。

     * 如"aaaab"對應的nextArr[4]=3;

     * 因爲前綴子串aaa...,後綴子串...aaa,最大匹配"aaa"長度爲3;

     * 如"abc1abc1" 則nextArr[7]=3

     * 前綴子串abc1ab...,後綴子串...bc1abc,最大匹配"abc"長度爲3。

     * <p>

     * KMP算法實現的分解:

     * S1:構建nextArr數組;<核心:一個while循環實現>

     * S2:逐個字符進行匹配,直到match字符完全匹配(找到),或str結束(未匹配到).<核心:一個while循環實現>

     * KMP算法的實現就是兩個while循環。

     * <p>

     * 仔細描述:

     * S1:構建nextArr數組:<這部分畫圖比較好理解>

     * 首先初始化,nextArr[0]=-1,nextArr[1]=0; -1標誌開始第一個字符的匹配;

     * index從2開始,nextArr[index]在nextArr[index-1]的基礎上推算:

     * cn表示nextArr[index-1]的值,即最大匹配的子串的長度;

     * A、 如果str[pos-1]==str[cn],則nextArr[i]=nextArr[i-1]+1;

     * B、 如果不相等,則更新cn,cn=nextArr[cn]; 然後繼續比較str[pos-1]==str[cn];

     * C、 如果cn等於0,則說明沒有匹配子串,直接賦值爲nextArr[i]=0.

     * <p>

     * 時間複雜度分析:由於只有在B情況下,pos不會自增,但是更新cn,cn的跳躍是比較大的,

     * 其實總體循環次數是不會超過2M的,因此時間複雜度可以認爲是O(M)。

     * S2: 已經有了nextArr數組,下面只需要逐個比較字符即可。

     * 當遇到字符不相等的時候,就需要將匹配字符串match右移動,最壞情況下,需要匹配N次,

     * 因此時間複雜度爲O(N);

     */

3、Java代碼

public int getFirstIndexOf(String str,String match){
    if(str == null || match == null || str.length() < 1 || str.length() < match.length()) return -1;
    char[] strChars = str.toCharArray();
    char[] matchChars = match.toCharArray();
    int sc_pos = 0,mc_pos = 0;
    int[] nextArray = getNextArray(matchChars);
    //while循環逐漸比較
    while(sc_pos < strChars.length && mc_pos < matchChars.length){
        if(strChars[sc_pos] == matchChars[mc_pos]){
            sc_pos++;
            mc_pos++;
        }else if(nextArray[mc_pos]==-1){//只有第一個纔會爲-1
            sc_pos++;
        }else{
            mc_pos = nextArray[mc_pos];//在match中繼續查找
        }
    }
    return mc_pos == matchChars.length ? sc_pos - matchChars.length : -1;
}

/**
 * 獲取nextArray
 * 利用while循環構建nextArray數組
 * 時間複雜度: O(2M) --> O(M)
 */
public int[] getNextArray(char[] match) {
    if (null == match || match.length == 0) {
        return new int[]{};
    } else if (match.length == 1) {
        return new int[]{-1};
    } else if (match.length == 2) {
        return new int[]{-1, 0};
    }
    int[] nextArray = new int[match.length];
    nextArray[0] = -1;
    nextArray[1] = 0;
    int pos = 2, cn = 0;
    //核心:while循環
    while (pos < match.length) {
        if (match[pos - 1] == match[cn]) {
            nextArray[pos++] = ++cn;//在前一個基礎上加1即可
        } else if (cn > 0) {
            cn = nextArray[cn];//改變cn爲cn位置處的值
        } else {
            nextArray[pos++] = 0;//沒有匹配
        }
    }
    return nextArray;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章