【译】Swift算法俱乐部-Boyer-Moore字符串搜索
本文是對 Swift Algorithm Club 翻譯的一篇文章。
Swift Algorithm Club是 raywenderlich.com網站出品的用Swift實現算法和數據結構的開源項目,目前在GitHub上有18000+??,我初略統計了一下,大概有一百左右個的算法和數據結構,基本上常見的都包含了,是iOSer學習算法和數據結構不錯的資源。
?andyRon/swift-algorithm-club-cn是我對Swift Algorithm Club,邊學習邊翻譯的項目。由于能力有限,如發現錯誤或翻譯不妥,請指正,歡迎pull request。也歡迎有興趣、有時間的小伙伴一起參與翻譯和學習?。當然也歡迎加??,?????。
本文的翻譯原文和代碼可以查看?swift-algorithm-club-cn/Boyer-Moore String Search
Boyer-Moore字符串搜索(Boyer-Moore String Search)
這個主題已經有教程 here
目標:在純Swift中編寫字符串搜索算法,而無需導入Foundation或使用NSString的rangeOfString()方法。
換句話說,我們想在String上實現一個indexOf(pattern:String)擴展,它返回在字符串里面第一次出現搜索模式的String.Index,如果找不到模式則返回nil 。
例子:
// Input: let s = "Hello, World" s.indexOf(pattern: "World")// Output: <String.Index?> 7// Input: let animals = "?????" animals.indexOf(pattern: "?")// Output: <String.Index?> 6 復制代碼注意: 牛的索引是6,而不是你想象的3,因為字符串為表情符號使用更多的存儲空間。String.Index的實際值并不那么重要,只是它指向字符串中的正確字符。
暴力方法工作正常,但效率不高,尤其是在大塊文本上。 事實證明,您不需要從源字符串中查看 每個 字符 —— 通常可以跳過多個字符。
這種跳過算法被稱為Boyer-Moore算法,它已存在很長時間了。它被認為是所有字符串搜索算法的基準。
以下是您在Swift中編寫它的方法:
extension String {func index(of pattern: String) -> Index? {let patternLength = pattern.countguard patternLength > 0, patternLength <= self.count else { return nil }var skipTable = [Character: Int]()for (i, c) in pattern.enumerated() {skipTable[c] = patternLength - i - 1}let p = pattern.index(before: pattern.endIndex)let lastChar = pattern[p]var i = index(startIndex, offsetBy: patternLength - 1)func backwards() -> Index? {var q = pvar j = iwhile q > pattern.startIndex {j = index(before: j)q = index(before: q)if self[j] != pattern[q] { return nil }}return j}while i < endIndex {let c = self[i]if c == lastChar {if let k = backwards() { return k }i = index(after: i)} else {i = index(i, offsetBy: skipTable[c] ?? patternLength, limitedBy: endIndex) ?? endIndex}}return nil} } 復制代碼該算法的工作原理如下。 讓源字符與搜索模式字符頭部對齊排列,并查看字符串中的哪個字符與搜索模式的 最后 字符匹配:
source string: Hello, World search pattern: World^ 復制代碼有三種可能性:
這兩個字符是相同的。 你找到了可能的匹配。
字符不相等,但源字符確實有出現在搜索模式其他位置中。
源字符完全沒有出現在搜索模式中。
在示例中,字符o和d不匹配,但o確實出現在搜索模式中。 這意味著我們可以跳過幾個位置:
source string: Hello, World search pattern: World^ 復制代碼注意兩個o字符現在是如何對齊的。 再次,您將搜索模式的最后一個字符與搜索文本進行比較:W vsd。 它們是不相同的,但W確實出現在搜索模式中。 因此,再次跳過一個位置,讓兩個W字符在相同位置:
source string: Hello, World search pattern: World^ 復制代碼這次兩個字符相等并且可能匹配。 要驗證匹配,您需要進行暴力搜索,但是從搜索模式的末尾開始向前搜索。 這就是它的全部。
在任何給定時間跳過的數量由“跳過表”確定,“跳過表”是搜索模式中所有字符的字典和跳過的數量。 示例中的跳過表如下所示:
W: 4 o: 3 r: 2 l: 1 d: 0 復制代碼字符越接近模式的末尾,跳過量越小。 如果某個字符在模式中出現多次,則最接近該模式結尾的字符將確定該字符的跳過值。
注意: 如果搜索模式只包含幾個字符,則執行暴力搜索會更快。 在構建跳過表與為短模式執行暴力搜索之間需要進行權衡。
致謝:此代碼基于1989年7月Dr Dobb's雜志發表的文章"Faster String Searches" by Costas Menico —— 對 ,1989年! 有時保留那些舊雜志是有用的。
擴展閱讀:這個算法的詳細分析
Boyer-Moore-Horspool 算法
上面算法的一個變體是Boyer-Moore-Horspool 算法。
像常規的Boyer-Moore算法一樣,它使用skipTable來跳過許多字符。 不同之處在于我們如何檢查局部匹配。在上面的版本中,如果找到了部分匹配,但不是完全匹配,我們只跳過一個字符。在這個修訂版本中,我們也在那種情況下使用跳過表。
這是Boyer-Moore-Horspool算法的一個實現:
extension String {func index(of pattern: String, usingHorspoolImprovement: Bool = false) -> Index? {let patternLength = pattern.countguard patternLength > 0, patternLength <= self.count else {return nil}var skipTable = [Character: Int]()for (i, c) in pattern.enumerated() {skipTable[c] = patternLength - i - 1}let p = pattern.index(before: pattern.endIndex)let lastChar = pattern[p]var i = index(startIndex, offsetBy: patternLength - 1)func backwards() -> Index? {var q = pvar j = iwhile q > pattern.startIndex {j = index(before: j)q = index(before: q)if self[j] != pattern[q] { return nil }}return j}while i < endIndex {let c = self[i]if c == lastChar {if let k = backwards() { return k }if !usingHorspoolImprovement {i = index(after: i)} else {let jumpOffset = max(skipTable[c] ?? patternLength, 1)i = index(i, offsetBy: jumpOffset, limitedBy: endIndex) ?? endIndex}} else {i = index(i, offsetBy: skipTable[c] ?? patternLength, limitedBy: endIndex) ?? endIndex}}return nil} } 復制代碼在實踐中,Horspool版本的算法往往比原始版本略好一些。 但是,這取決于你愿意做出什么樣的權衡。
致謝:此代碼基于論文:R. N. Horspool (1980). "Practical fast searching in strings". Software - Practice & Experience 10 (6): 501–506.
作者:Matthijs Hollemans,Andreas Neusü?,Matías Mazzei
翻譯:Andy Ron
校對:Andy Ron
譯注: 阮一峰老師的文章 字符串匹配的Boyer-Moore算法 講的比較清晰。
總結
以上是生活随笔為你收集整理的【译】Swift算法俱乐部-Boyer-Moore字符串搜索的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OSPF(三)
- 下一篇: Purism 宣布推出 PureOS 应