You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: Boyer-Moore/README.markdown
+59-33
Original file line number
Diff line number
Diff line change
@@ -87,7 +87,7 @@ extension String {
87
87
// pattern, we can skip ahead by the full pattern length. However, if
88
88
// the character *is* present in the pattern, there may be a match up
89
89
// ahead and we can't skip as far.
90
-
i =self.index(i, offsetBy: skipTable[c] ?? patternLength)
90
+
i =index(i, offsetBy: skipTable[c] ?? patternLength, limitedBy: endIndex) ?? endIndex
91
91
}
92
92
}
93
93
returnnil
@@ -158,40 +158,66 @@ Here's an implementation of the Boyer-Moore-Horspool algorithm:
158
158
```swift
159
159
extensionString {
160
160
funcindexOf(pattern: String) ->String.Index? {
161
-
let patternLength = pattern.characters.count
162
-
assert(patternLength >0)
163
-
assert(patternLength <=self.characters.count)
161
+
// Cache the length of the search pattern because we're going to
162
+
// use it a few times and it's expensive to calculate.
163
+
let patternLength = pattern.characters.count
164
+
assert(patternLength >0)
165
+
assert(patternLength <=self.characters.count)
164
166
165
-
var skipTable = [Character:Int]()
166
-
for (i, c) in pattern.characters.enumerated() {
167
-
skipTable[c] = patternLength - i -1
168
-
}
167
+
// Make the skip table. This table determines how far we skip ahead
168
+
// when a character from the pattern is found.
169
+
var skipTable = [Character:Int]()
170
+
for (i, c) in pattern.characters.enumerated() {
171
+
skipTable[c] = patternLength - i -1
172
+
}
169
173
170
-
let p = pattern.index(before: pattern.endIndex)
171
-
let lastChar = pattern[p]
172
-
var i =self.index(startIndex, offsetBy: patternLength -1)
173
-
174
-
funcbackwards() ->String.Index? {
175
-
var q = p
176
-
var j = i
177
-
while q > pattern.startIndex {
178
-
j =index(before: j)
179
-
q =index(before: q)
180
-
ifself[j] != pattern[q] { returnnil }
181
-
}
182
-
return j
183
-
}
174
+
// This points at the last character in the pattern.
175
+
let p = pattern.index(before: pattern.endIndex)
176
+
let lastChar = pattern[p]
184
177
185
-
while i <self.endIndex {
186
-
let c =self[i]
187
-
if c == lastChar {
188
-
iflet k =backwards() { return k }
189
-
i =index(after: i)
190
-
} else {
191
-
i =index(i, offsetBy: skipTable[c] ?? patternLength)
192
-
}
193
-
}
194
-
returnnil
178
+
// The pattern is scanned right-to-left, so skip ahead in the string by
179
+
// the length of the pattern. (Minus 1 because startIndex already points
180
+
// at the first character in the source string.)
181
+
var i =self.index(startIndex, offsetBy: patternLength -1)
182
+
183
+
// This is a helper function that steps backwards through both strings
184
+
// until we find a character that doesn’t match, or until we’ve reached
185
+
// the beginning of the pattern.
186
+
funcbackwards() ->String.Index? {
187
+
var q = p
188
+
var j = i
189
+
while q > pattern.startIndex {
190
+
j =index(before: j)
191
+
q =index(before: q)
192
+
ifself[j] != pattern[q] { returnnil }
193
+
}
194
+
return j
195
+
}
196
+
197
+
// The main loop. Keep going until the end of the string is reached.
198
+
while i <self.endIndex {
199
+
let c =self[i]
200
+
201
+
// Does the current character match the last character from the pattern?
202
+
if c == lastChar {
203
+
204
+
// There is a possible match. Do a brute-force search backwards.
205
+
iflet k =backwards() { return k }
206
+
207
+
// Ensure to jump at least one character (this is needed because the first
208
+
// character is in the skipTable, and `skipTable[lastChar] = 0`)
209
+
let jumpOffset =max(skipTable[c] ?? patternLength, 1)
210
+
i =index(i, offsetBy: jumpOffset, limitedBy: endIndex) ?? endIndex
211
+
} else {
212
+
// The characters are not equal, so skip ahead. The amount to skip is
213
+
// determined by the skip table. If the character is not present in the
214
+
// pattern, we can skip ahead by the full pattern length. However, if
215
+
// the character *is* present in the pattern, there may be a match up
216
+
// ahead and we can't skip as far.
217
+
i =index(i, offsetBy: skipTable[c] ?? patternLength, limitedBy: endIndex) ?? endIndex
218
+
}
219
+
}
220
+
returnnil
195
221
}
196
222
}
197
223
```
@@ -200,4 +226,4 @@ In practice, the Horspool version of the algorithm tends to perform a little bet
200
226
201
227
Credits: This code is based on the paper: [R. N. Horspool (1980). "Practical fast searching in strings". Software - Practice & Experience 10 (6): 501–506.](http://www.cin.br/~paguso/courses/if767/bib/Horspool_1980.pdf)
202
228
203
-
_Written for Swift Algorithm Club by Matthijs Hollemans, updated by Andreas Neusüß_
229
+
_Written for Swift Algorithm Club by Matthijs Hollemans, updated by Andreas Neusüß_, [Matías Mazzei](https://github.com/mmazzei).
0 commit comments