KMP算法詳解

KMP是解決兩個字符串匹配問題的非常好的算法撒轮,算法的時間復(fù)雜是O(n)产捞。

現(xiàn)在假設(shè)場景是兩個字符串str1醇锚,str2,求str2是否是str1的子串坯临,如果是焊唬,返回第一個子串的第一個字母在str1中的索引。

例如:str1 = "ababcababtk";str2 = "ababtk";則返回5看靠。

要使用KMP算法赶促,首先要算出用來加速匹配的str2的數(shù)組next;

一挟炬、next數(shù)組的求解

next[n]中裝的是str2中索引為n的字符前面的子串substr2前綴與后綴相等的最長前綴的長度鸥滨,前綴與后綴的要求是,前綴一定要包含子串substr2的第一個字符谤祖,但是不能包含最后一個字符婿滓,后綴一定要包含子串substr2的最后一個字符,但是不能包含最后一個字符粥喜。

比如對于字符串str2中的't'前面的子串就是abab凸主,它的前綴是"a","ab","aba";后綴是"b","ab","bab"额湘。前綴與后綴相等的最長前綴的長度是2卿吐,所以't'對應(yīng)的next數(shù)組的值是2,即next[4] = 2锋华。

規(guī)定next[0] = -1,next[1] = 0,這是人為規(guī)定的嗡官,當(dāng)然如果str2的長度為1,那么next數(shù)組就只有一個-1供置。

下面介紹怎么利用前面的next值求后面的next的值谨湘。

假設(shè)str中n-1位置求出的最長相等前后綴長度是m,要求n位置的最長相等前后綴長度即next[n]芥丧,下面為了方便表述紧阔,將str字符串看成一個str字符數(shù)組。

1续担、str[n-1] = str[m],如下圖擅耽,n-1和m的位置都是'k',所以'b'對應(yīng)的next[n] = m+1;

str[n-1] = str[m]

2物遇、str[n-1] != str[m],如下圖乖仇,則看字符't'對應(yīng)的next[m],假設(shè)next[m] = u,如果next[u]=next[m],則'b'對應(yīng)的next[n] = u+1,否則再看next[u],按照上述步驟繼續(xù)進(jìn)行憾儒,直到next[x] = -1為止,此時next[n] = 0;

str[n-1] != str[m]

使用上述方法求出的str2的next數(shù)組是[-1,0,0,1,2,0]乃沙。


public static int[] getNext(String str){
        // 如果str長度為1起趾,直接返回只含-1的數(shù)組
        if(str.length() == 1){
            return new int[]{-1};
        }
        int[] next = new int[str.length()];
        char[] str2 = str.toCharArray();
        // 首先將人為設(shè)定最長相等前綴的值填好
        next[0] = -1;
        next[1] = 0;
        int n = 2;
        // m代表i前面一個字符的最長相等前綴的長度,最開始i=2,next[i-1] = 0,所以n的初始值是0警儒;
        int m = 0;
        while(n < next.length){
             // 這是情況一训裆,str[n-1] = str[m],此時next[n] = m+1
            if(str2[m] == str2[n-1]){
                next[n++] = ++m;
             // 這是情況二,str[n-1] != str[m],此時蜀铲,m = next[m],再次比較
            }else if(m > 0){
                m = next[m];
                // 當(dāng)m=0時,則next[n] = 0;
            }else{
                next[n++] = 0;
            }
        }
        return next;
    }

二边琉、使用next數(shù)組加速匹配

還是上述問題,str1 = "ababcababtk"记劝;str2 = "ababtk"变姨,上面已經(jīng)求出str2的next數(shù)組是[-1,0,0,1,2,0],p1是指向str1當(dāng)前匹配位置的指針厌丑,p2是指向str2當(dāng)前位置的指針定欧,開始匹配時會出現(xiàn)下面的情況。為了方便描述蹄衷,還是將str1和str2看成兩個字符數(shù)組忧额。

1、str1[p1] == str2[p2] 愧口,則p1和p2都往后移一位。

str1[p1] == str2[p2]

2类茂、str1[p1] != str2[p2]且p2 != 0耍属,則p2 = next[p2],p1不動巩检。

str1[p1] != str2[p2]且p2 != 0

3厚骗、str1[p1] != str2[p2]且p2==0,p2不移動兢哭,p1向后移動一位领舰。

str1[p1] != str2[p2]且p2==0

如果p2 = str2.length,停止上述過程迟螺,此時子串的第一個字符在str1中的位置是p1-p2冲秽,否則直到str1遍歷完,之后若p2 != str2.length矩父,則str1中不包含str2锉桑,否則子串的第一個字符在str1中的位置是p1-p2。

完整代碼實現(xiàn)如下:

public static void main(String[] args) {
        String str1 = "ababcababtk";
        String str2 = "ababtk";
        int[] next = getNext(str2);
        int res = KMP(str1,str2,next);
        System.out.println(res);
    }

    public static int KMP(String str1,String str2,int[] next){
        if(str1 == null || str2 == null || str2.length() < 1 || str1.length() < str2.length()){
            return -1;
        }
        char[] strArr1 = str1.toCharArray();
        char[] strArr2 = str2.toCharArray();
        int p1 = 0;
        int p2 = 0;
        while(p1 < strArr1.length && p2 < strArr2.length){
            // 情況1窍株,strArr1[p1] == strArr2[p2] 民轴,則p1和p2都往后移一位
            if(strArr1[p1] == strArr2[p2]){
                p1++;
                p2++;
            // 情況3攻柠,str1[p1] != str2[p2]且p2==0,p2不移動后裸,p1向后移動一位
            }else if(p2 == 0){
                p1++;
            // 情況2瑰钮,str1[p1] != str2[p2]且p2!=0,則p2 = next[p2]微驶,p1不動飞涂。
            }else{
                p2 = next[p2];
            }
        }
        return p2 == strArr2.length ? p1-p2:-1;
    }

    public static int[] getNext(String str){
        // 如果str長度為1,直接返回只含-1的數(shù)組
        if(str.length() == 1){
            return new int[]{-1};
        }
        int[] next = new int[str.length()];
        char[] str2 = str.toCharArray();
        // 首先將人為設(shè)定最長相等前綴的值填好
        next[0] = -1;
        next[1] = 0;
        int n = 2;
        // m代表i前面一個字符的最長相等前綴的長度祈搜,最開始i=2,next[i-1] = 0,所以n的初始值是0较店;
        int m = 0;
        while(n < next.length){
             // 這是情況一,str[n-1] = str[m],此時next[n] = m+1
            if(str2[m] == str2[n-1]){
                next[n++] = ++m;
             // 這是情況二容燕,str[n-1] != str[m],此時梁呈,m = next[m],再次比較
            }else if(m > 0){
                m = next[m];
                // 當(dāng)m=0時,則next[n] = 0;
            }else{
                next[n++] = 0;
            }
        }
        return next;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蘸秘,隨后出現(xiàn)的幾起案子官卡,更是在濱河造成了極大的恐慌,老刑警劉巖醋虏,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寻咒,死亡現(xiàn)場離奇詭異,居然都是意外死亡颈嚼,警方通過查閱死者的電腦和手機(jī)毛秘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阻课,“玉大人叫挟,你說我怎么就攤上這事∠奚罚” “怎么了抹恳?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長署驻。 經(jīng)常有香客問我奋献,道長,這世上最難降的妖魔是什么旺上? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任瓶蚂,我火速辦了婚禮,結(jié)果婚禮上抚官,老公的妹妹穿的比我還像新娘扬跋。我一直安慰自己,他們只是感情好凌节,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布钦听。 她就那樣靜靜地躺著洒试,像睡著了一般。 火紅的嫁衣襯著肌膚如雪朴上。 梳的紋絲不亂的頭發(fā)上垒棋,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音痪宰,去河邊找鬼叼架。 笑死,一個胖子當(dāng)著我的面吹牛衣撬,可吹牛的內(nèi)容都是我干的乖订。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼具练,長吁一口氣:“原來是場噩夢啊……” “哼乍构!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扛点,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤哥遮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后陵究,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眠饮,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年铜邮,在試婚紗的時候發(fā)現(xiàn)自己被綠了仪召。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡牲距,死狀恐怖返咱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牍鞠,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布评姨,位于F島的核電站难述,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吐句。R本人自食惡果不足惜胁后,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嗦枢。 院中可真熱鬧攀芯,春花似錦、人聲如沸文虏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至年鸳,卻和暖如春趴久,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搔确。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工彼棍, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膳算。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓座硕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親涕蜂。 傳聞我的和親對象是個殘疾皇子华匾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容