Swift-從字符串匹配看普通算法與KMP算法

最近在leetcode上刷題,當然熔萧,是用swift,中間的辛酸經(jīng)歷就不提了贮缕,不得不說swift在便利性上的確十分強大俺榆,但其效率也的確相較C++、JAVA等顯得相對低下罐脊,在這里不得不吐槽leetcode的Time Limit Exceeded魔咒似乎并不隨著語言環(huán)境的不同而有所改變蜕琴,每當看著Top solutions上一些C++凌简、JAVA信徒用同樣的算法打敗了Time Limit Exceeded魔咒而我卻一次又一次在魔咒面前止步不得不說我感到我的心在滴血恃逻。

然而就是又一次在刷題苦于ime Limit Exceeded魔咒時看到了KMP算法,當即被晦澀難懂的理論砸的懵逼了寇损,在好半天的理解加題目應用才逐漸明白過來矛市。

KMP算法的原理可以看這篇,在這么多篇的博文中這篇算是我看過最淺顯易懂的了尘盼。

我只是將KMP算法的原理通過實際題目以自我的理解剖析一遍。


算法題是這樣的:
Repeated Substring Pattern

Given a non-empty string check if it can be constructed by taking a substring of it and appending multiple copies of the substring together. You may assume the given string consists of lowercase English letters only and its length will not exceed 10000.

Example 1:

Input: "abab"
Output: True

Explanation: It's the substring "ab" twice.

Example 2:

Input: "aba"
Output: False

Example 3:

Input: "abcabcabcabc"
Output: True

Explanation: It's the substring "abc" four times. (And the substring "abcabc" twice.)

簡單明了的算法題配紫,給你一個不為空的字符串午阵,你需要通過算法得出這個字符串中是否有字符成循環(huán)關系,返回布爾值植袍。

簡單的審題我們可以寫成這樣的偽代碼籽懦。

//for循環(huán)套for循環(huán)。
//外層的循環(huán)從字符串的0下標開始截取每一個字符組成字符串暮顺。
//內層的循環(huán)從外層字符串的長度+1開始循環(huán),截取之后的每個字符組成字符串并與外層的可變字符進行比較
//定義一個變量羽氮,當內層字符串與外層字符串相同時+=1惫恼,若變量在跳出內層循環(huán)時的值與原始字符串的長度 / 外層字符串的長度 - 1(外層字符串的長度) 的值相等則輸出True,否則輸出False令宿。

通過偽代碼你即可看出這是多繁復的寫法了,當然我們可以通過加上一些條件進行篩選而減少步驟掀淘,例如

//若外層字符串與內層字符串長度不相等則不進行比較
//若外層字符串長度 % 原始字符串長度不為0則不進行內部循環(huán)

等等油昂。
這些篩選條件的確讓內部操作減少但也讓代碼變得更加繁瑣,而且就算這樣也沒抵擋地住Time Limit Exceeded魔咒拦惋。

那么安寺,換一種思考法,我們通過截取字符串并讓它循環(huán)增殖再與原字符串比較挑庶,如此,是否更高效举畸?

func repeatedSubstringPattern(_ str: String) -> Bool {
    for i in (1...str.characters.count/2).reversed(){
        if str.characters.count % i == 0 {
            let s = str.substring(to: str.index(str.startIndex, offsetBy: i))
            var s2 = ""
            let m = str.characters.count / s.characters.count
            for _ in 0..<m{
                s2.append(s)
            }
            if s2 == str { return true }
        }
    }
    return false
}

這種方法比起第一種擁有更簡潔的代碼凳枝,并且,其效率相較于第一種也顯得高上一節(jié)(你看外部for循環(huán)都砍一半了叛买,內部for循環(huán)更是只有m次)蹋订。
不得不說Swift的某些字符串操作手法簡直是天怒人怨,截取字符串要寫成這樣

            let s = str.substring(to: str.index(str.startIndex, offsetBy: i))

如果要獲得某一個具體位置的字符更是要如此

        let sc = str.characters[str.characters.index(str.characters.startIndex, offsetBy: i)]

不要說 for c in s.characters椒功,兩個字符串比較時還是要加個變量記錄位置ORZ

然而玫锋,終究還是抵擋不住魔咒,雖然第二種方法效率高上一截撩鹿,但依然抵擋不住魔咒,(順便一提同樣的代碼我寫了份C++的直接通過了……)
于是在Top solutions我找到了KMP算法

func repeatedSubstringPattern1(_ str: String) -> Bool {
    //KMP算法
    let length = str.characters.count
    //i表示的是下一需要比較的字符串的下標
    var i = 1,j = 0
    var next = [Int](repeating:0, count:length+1)
    while i < length {
        if str.characters[str.characters.index(str.characters.startIndex, offsetBy: i)] == str.characters[str.characters.index(str.characters.startIndex, offsetBy: j)] {
            //若位置i的字符與j匹配的上則對下一個位置的字符進行比較
            //同時键思,對next數(shù)組(即最大公共長度數(shù)組進行更新),其下標中的數(shù)值即是當前的最大公共長度看蚜,即j+=1后的值
            i+=1
            j+=1
            next[i] = j
        } else if j == 0 {
            //若公共長度為一赔桌,這種情況可能出現(xiàn)在一開始,也就是字符一開始沒有匹配上的時候疾党,亦可能出現(xiàn)在匹配過程中因為匹配不上而不斷切割公共長度,直到公共長度為0時竭钝。
            //此時雹洗,將需要比對的字符向后移動一位進行匹配,也就是i+=1
            i+=1
        } else {
            //當字符不匹配而公共長度又不為0時則不斷切割最大公共長度时肿,即前移j在字符串中的位置,也就是將之前保存的next容器中的最大公共長度進行切割港令,獲取j的歷史值
            j = next[j]
        }
    }
    return  next[length] > 0 && next[length] % (length - next[length]) == 0
}

哈哈哈哈锈颗!龜兒子,就這樣也想難倒我淋淀!

……


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末朵纷,一起剝皮案震驚了整個濱河市永脓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌常摧,老刑警劉巖威创,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肚豺,死亡現(xiàn)場離奇詭異界拦,居然都是意外死亡,警方通過查閱死者的電腦和手機享甸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門枪萄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瓷翻,你說我怎么就攤上這事割坠。” “怎么了对妄?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵敢朱,是天一觀的道長。 經(jīng)常有香客問我孝常,道長蚓哩,這世上最難降的妖魔是什么构灸? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任喜颁,我火速辦了婚禮曹阔,結果婚禮上,老公的妹妹穿的比我還像新娘赃份。我一直安慰自己,他們只是感情好漓库,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痢士,像睡著了一般茂装。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上少态,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天彼妻,我揣著相機與錄音,去河邊找鬼侨歉。 笑死,一個胖子當著我的面吹牛炮温,可吹牛的內容都是我干的牵舵。 我是一名探鬼主播柒啤,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼担巩,長吁一口氣:“原來是場噩夢啊……” “哼重斑!你這毒婦竟也來了?” 一聲冷哼從身側響起窥浪,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤漾脂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后骨稿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姜钳,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡哥桥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年激涤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倦踢。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡辱挥,死狀恐怖,靈堂內的尸體忽然破棺而出晤碘,到底是詐尸還是另有隱情,我是刑警寧澤蕊梧,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布腮介,位于F島的核電站端衰,受9級特大地震影響,放射性物質發(fā)生泄漏旅东。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一腾节、第九天 我趴在偏房一處隱蔽的房頂上張望荤牍。 院中可真熱鬧,春花似錦康吵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽响巢。三九已至棒妨,卻和暖如春踪古,著一層夾襖步出監(jiān)牢的瞬間靶衍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工蜈出, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涛酗,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓燕刻,卻偏偏與公主長得像剖笙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子弥咪,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內容