LeetCode Prob.28 Inplement strStr()

自帶的.find()方法

找字串的位置,這種事情,偷懶的話很好解決:

if not needle: return -1
return haystack.find(needle)

也算是學(xué)了學(xué)string方法贾铝。

然而也應(yīng)該借此機會學(xué)一學(xué)KMP吼拥。

KMP雖然看起來代碼很少,但是其中的道理還真是一時半會理不清楚的褪尝。

暴力的辦法

暴力的辦法很簡單亏推,對于一個給定的字符串起始位置 i ,只需要逐個比對haystack[i:i + len(haystack)]needle是否相等就可以了叁巨。然后遍歷整個range(i - len(haystack)斑匪。

但是這樣的話,就存在一個問題锋勺,信息的利用太少了蚀瘸。

比如,ABDABC...去匹配ABDABD...庶橱,實際上贮勃,當我們匹配到第六個字母CD不正確的時候,我們已經(jīng)不必再往下匹配BDABD...了悬包,而是可以直接匹配ABD...

出現(xiàn)了不匹配:
√√√√√↓
ABDABC...
ABDABD...
√√√√√↑

暴力解法:
 ↓
ABDABC...
 ABDABD...
 ↑

優(yōu)化方法:
   √√↓
ABDABC...
   ABDABD...
   √√↑

如果我們能好好整理一下這種樸素的優(yōu)化邏輯衙猪,就可以提高算法的速度。這其實就是KMP算法的基本思想布近。

KMP算法

next數(shù)組的含義

仔細觀察一下上面的例子:

   √√↓
ABDABCXXX...
   ABDABD...
   √√↑

我們是怎樣知道垫释,可以直接移動needle到這里,而且同時知道已經(jīng)有兩個位是匹配了的呢撑瞧?

這里就涉及了KMP算法的一個精髓部分:next數(shù)組棵譬。

首先,它將我們上面的行為總結(jié)為這個樣子:


  1. 如果在匹配中预伺,出現(xiàn)了這樣的情況:
 √   √   √  ↓
[A][...][A][B]
[A][...][A][C]
 √   √   √  ↑

其中订咸,[A][...][A]是經(jīng)過比較,已經(jīng)相同的部分酬诀;而[A]是相同的前后綴脏嚷。

  1. 那么,我們就可以將needle前綴[A]移動到后綴[A]的地方:
         √  ↓
[A][...][A][B]
        [A][...]
         √   ↑

然后讓[B]和[...]繼續(xù)進行比較瞒御。

  1. 例如:
 √√  √  √√  ↓
[AB][D][AB][C]
[AB][D][AB][D]
 √√  √  √√  ↑

我們只需移動[AB]即可:

        √√  ↓
[AB][D][AB][C...]
       [AB][D][AB][D...]
        √√  ↑

而對于needle的每一個位父叙,我們都可以先找出這一位之前的字串里,相等前后綴的長度肴裙,然后放入next數(shù)組趾唱,指導(dǎo)KMP核心算法指針的回溯位置。

比如:

對于'ABDABD...'

>> next[3] == ?
[][ABD][][A...]
因此next[3] == 0

>> next[4] == ?
[A][BD][A][B...]
因此next[4] == 1

>> next[5] == ?
[AB][D][AB][D...]
因此next[5] == 2

next數(shù)組的實現(xiàn)方式

首先蜻懦,雖然next[i] == 0代表回溯位置是0甜癞,我們還需要約定next[0] == -1而不是0,以此作為字符串開始的標志宛乃。然后我們討論邏輯悠咱,再討論代碼實現(xiàn)蒸辆。

邏輯部分

初始狀態(tài)

就普通的情況而言,我們的狀態(tài)是這樣的:

[A]X...[A]Y...
   ↑      ↑
   p      s

其中[A]是經(jīng)過比較乔煞,已經(jīng)相同了的部分吁朦。

XY是什么關(guān)系?

  1. X == Y渡贾,則p, s都向前移動逗宜,next[s] = len([AX]),也就是next[s] = p空骚。
[AX]Z...[AY]Q...
    ↑       ↑
    p       s
  1. X != Y纺讲,那么我們將[A]展開看看:
[B][Z...][B]X...[B][Z...][B]Y
            ↑               ↑
            p               s

發(fā)現(xiàn)了嗎?我們只需要將p移動到第一個[B]之后囤屹,也就是next[p]處就行了熬甚!

[B][Z...][B]X...[B][Z...][B]Y
    ↑                       ↑
    p                       s

然后,我們就相當于回到了開頭的狀態(tài)肋坚,換個字母就是[C]Z...[C]Y...乡括。

邊界條件處理

我們之所以要約定next[0] == -1,就是因為智厌,當p == 0X != Y的時候:

X...Y
↑   ↑
p   s

根據(jù)上面的定義诲泌,我們會令p = next[0]。然而X前面已經(jīng)沒有字符了铣鹏,所以我們需要特殊處理敷扫。當然,特判這個情況诚卸,讓s += 1是可以的葵第,但是用我們目前的約定方法,就可以套用X == Y情況的代碼了合溺。具體操作可以看看代碼部分卒密。

此外,s的遍歷空間是什么呢棠赛?考慮到X == Y的情況下需要先增加s再賦值哮奇,所以應(yīng)該是range(len(needle) - 1)

代碼部分

    def calNext(needle): 
        prept = -1
        sufpt = 0
        nlen = len(needle)
        next = [-1] *  nlen
        while sufpt < nlen - 1:
            if prept == -1 or needle[prept] == needle[sufpt]:
                prept += 1
                sufpt += 1
                if needle[prept] != needle[sufpt]:
                    next[sufpt] = prept
                else:
                    next[sufpt] = next[prept] # 可以想想這個優(yōu)化的意義
            else:
                prept = next[prept]
        return next

KMP算法的本體

如果你看懂了上面關(guān)于next數(shù)組的原理恭朗,那么只需要這點代碼,你大概就能知道KMP的實現(xiàn)方式了:

hpt = npt = 1
while hpt < hlen and npt < nlen:
        if npt == -1 or haystack[hpt] == needle[npt]:
            hpt += 1
            npt += 1
        else:
            npt = next[npt]
    if patpt == plen: return strpt - patpt
    else: return -1
  • if hpt == -1和next部分的算法一樣依疼,是為了挪動hpt而不挪動已經(jīng)位于首端的npt痰腮。
  • if haystack[hpt] == needle[npt]就是正常的字符匹配成功。
  • 為了防止你忘記律罢,else就是我們在next數(shù)組的含義部分的開頭所說的膀值,移動needle前綴移到相同后綴位置:
        √√  ↓
[AB][D][AB][C...]
       [AB][D][AB][D...]
        √√  ↑

整個KMP算法到此就結(jié)束了棍丐!雖然代碼很短,但是可以說非常精妙了沧踏!

有興趣的話歌逢,可以繼續(xù)了解BM算法和Sunday算法,這個鏈接也是講解KMP算法的翘狱,而且圖片也比較多秘案,可以一看。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末潦匈,一起剝皮案震驚了整個濱河市阱高,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茬缩,老刑警劉巖赤惊,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異凰锡,居然都是意外死亡未舟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門掂为,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裕膀,“玉大人,你說我怎么就攤上這事菩掏』杲牵” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵智绸,是天一觀的道長野揪。 經(jīng)常有香客問我,道長瞧栗,這世上最難降的妖魔是什么斯稳? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮迹恐,結(jié)果婚禮上挣惰,老公的妹妹穿的比我還像新娘。我一直安慰自己殴边,他們只是感情好憎茂,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锤岸,像睡著了一般竖幔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上是偷,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天拳氢,我揣著相機與錄音募逞,去河邊找鬼。 笑死馋评,一個胖子當著我的面吹牛放接,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播留特,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼纠脾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了磕秤?” 一聲冷哼從身側(cè)響起乳乌,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎市咆,沒想到半個月后汉操,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蒙兰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年磷瘤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搜变。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡采缚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挠他,到底是詐尸還是另有隱情扳抽,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布殖侵,位于F島的核電站贸呢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拢军。R本人自食惡果不足惜楞陷,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望茉唉。 院中可真熱鬧固蛾,春花似錦、人聲如沸度陆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽懂傀。三九已至趾诗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸿竖,已是汗流浹背沧竟。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缚忧,地道東北人悟泵。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像闪水,于是被迫代替她去往敵國和親糕非。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

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

  • <center>#1 Two Sum</center> link Description:Given an arr...
    鐺鐺鐺clark閱讀 2,140評論 0 3
  • My code: My test result: 這道題目要求是用KMP算法來做的球榆。具體KMP算法是什么東西朽肥,就看...
    Richardo92閱讀 385評論 0 1
  • PMI穩(wěn)如泰山,堅不可摧持钉,期待你的加入衡招,自己排單就有收入∶壳浚看懂了始腾,會整天偷笑。PMI真的是互聯(lián)網(wǎng)上最好的項目空执。
    讀你_ae06閱讀 175評論 0 1
  • 文章作者:Tyan博客:noahsnail.com | CSDN | 簡書 1. Description 2. S...
    SnailTyan閱讀 233評論 0 0
  • 2017年8月15日 星期二 晴 早上站樁時間保持在二十五分鐘左右浪箭,今天兩腿基本沒有振抖,感覺很舒適辨绊,右半身汗液冒...
    愛蓮_8f0d閱讀 384評論 0 0