Skip to content

Commit 61a12a2

Browse files
authored
Merge pull request kodecocodes#248 from KVTaniguchi/Boyer-Moore_Swift3
Boyer moore swift3
2 parents 182e51b + f09b42b commit 61a12a2

File tree

3 files changed

+85
-63
lines changed

3 files changed

+85
-63
lines changed

Boyer-Moore/BoyerMoore.playground/Contents.swift

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@ extension String {
77
assert(patternLength <= self.characters.count)
88

99
var skipTable = [Character: Int]()
10-
for (i, c) in pattern.characters.enumerate() {
10+
for (i, c) in pattern.characters.enumerated() {
1111
skipTable[c] = patternLength - i - 1
1212
}
1313

14-
let p = pattern.endIndex.predecessor()
14+
let p = pattern.index(before: pattern.endIndex)
1515
let lastChar = pattern[p]
16-
var i = self.startIndex.advancedBy(patternLength - 1)
16+
var i = self.index(startIndex, offsetBy: patternLength - 1)
1717

1818
func backwards() -> String.Index? {
1919
var q = p
2020
var j = i
2121
while q > pattern.startIndex {
22-
j = j.predecessor()
23-
q = q.predecessor()
22+
j = index(before: j)
23+
q = index(before: q)
2424
if self[j] != pattern[q] { return nil }
2525
}
2626
return j
@@ -30,21 +30,19 @@ extension String {
3030
let c = self[i]
3131
if c == lastChar {
3232
if let k = backwards() { return k }
33-
i = i.successor()
33+
i = index(after: i)
3434
} else {
35-
i = i.advancedBy(skipTable[c] ?? patternLength)
35+
i = index(i, offsetBy: skipTable[c] ?? patternLength)
3636
}
3737
}
3838
return nil
3939
}
4040
}
4141

42-
43-
4442
// A few simple tests
4543

4644
let s = "Hello, World"
47-
s.indexOf("World") // 7
45+
s.indexOf(pattern: "World") // 7
4846

4947
let animals = "🐶🐔🐷🐮🐱"
50-
animals.indexOf("🐮") // 6
48+
animals.indexOf(pattern: "🐮") // 6

Boyer-Moore/BoyerMoore.playground/timeline.xctimeline

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,20 @@
22
<Timeline
33
version = "3.0">
44
<TimelineItems>
5+
<LoggerValueHistoryTimelineItem
6+
documentLocation = "#CharacterRangeLen=1&amp;CharacterRangeLoc=345&amp;EndingColumnNumber=37&amp;EndingLineNumber=9&amp;StartingColumnNumber=9&amp;StartingLineNumber=9&amp;Timestamp=497585369.829471"
7+
selectedRepresentationIndex = "0"
8+
shouldTrackSuperviewWidth = "NO">
9+
</LoggerValueHistoryTimelineItem>
10+
<LoggerValueHistoryTimelineItem
11+
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=345&amp;EndingColumnNumber=26&amp;EndingLineNumber=9&amp;StartingColumnNumber=9&amp;StartingLineNumber=9&amp;Timestamp=497585369.82964"
12+
selectedRepresentationIndex = "0"
13+
shouldTrackSuperviewWidth = "NO">
14+
</LoggerValueHistoryTimelineItem>
15+
<LoggerValueHistoryTimelineItem
16+
documentLocation = "#CharacterRangeLen=1&amp;CharacterRangeLoc=345&amp;EndingColumnNumber=25&amp;EndingLineNumber=9&amp;StartingColumnNumber=9&amp;StartingLineNumber=9&amp;Timestamp=497585369.82978"
17+
selectedRepresentationIndex = "0"
18+
shouldTrackSuperviewWidth = "NO">
19+
</LoggerValueHistoryTimelineItem>
520
</TimelineItems>
621
</Timeline>

Boyer-Moore/README.markdown

Lines changed: 61 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ For example:
99
```swift
1010
// Input:
1111
let s = "Hello, World"
12-
s.indexOf("World")
12+
s.indexOf(pattern: "World")
1313

1414
// Output:
1515
<String.Index?> 7
1616

1717
// Input:
1818
let animals = "🐶🐔🐷🐮🐱"
19-
animals.indexOf("🐮")
19+
animals.indexOf(pattern: "🐮")
2020

2121
// Output:
2222
<String.Index?> 6
@@ -32,7 +32,7 @@ Here's how you could write it in Swift:
3232

3333
```swift
3434
extension String {
35-
func indexOf(pattern: String) -> String.Index? {
35+
func indexOf(pattern: String) -> String.Index? {
3636
// Cache the length of the search pattern because we're going to
3737
// use it a few times and it's expensive to calculate.
3838
let patternLength = pattern.characters.count
@@ -42,57 +42,58 @@ extension String {
4242
// Make the skip table. This table determines how far we skip ahead
4343
// when a character from the pattern is found.
4444
var skipTable = [Character: Int]()
45-
for (i, c) in pattern.characters.enumerate() {
46-
skipTable[c] = patternLength - i - 1
45+
for (i, c) in pattern.characters.enumerated() {
46+
skipTable[c] = patternLength - i - 1
4747
}
4848

4949
// This points at the last character in the pattern.
50-
let p = pattern.endIndex.predecessor()
50+
let p = index(before: pattern.endIndex)
5151
let lastChar = pattern[p]
5252

5353
// The pattern is scanned right-to-left, so skip ahead in the string by
5454
// the length of the pattern. (Minus 1 because startIndex already points
5555
// at the first character in the source string.)
56-
var i = self.startIndex.advancedBy(patternLength - 1)
56+
var i = index(self.startIndex, offsetBy: patternLength - 1)
5757

58-
// This is a helper function that steps backwards through both strings
58+
// This is a helper function that steps backwards through both strings
5959
// until we find a character that doesn’t match, or until we’ve reached
6060
// the beginning of the pattern.
6161
func backwards() -> String.Index? {
62-
var q = p
63-
var j = i
64-
while q > pattern.startIndex {
65-
j = j.predecessor()
66-
q = q.predecessor()
67-
if self[j] != pattern[q] { return nil }
68-
}
69-
return j
62+
var q = p
63+
var j = i
64+
while q > pattern.startIndex {
65+
j = index(before: j)
66+
q = index(before: q)
67+
if self[j] != pattern[q] { return nil }
68+
}
69+
return j
7070
}
7171

7272
// The main loop. Keep going until the end of the string is reached.
7373
while i < self.endIndex {
74-
let c = self[i]
75-
76-
// Does the current character match the last character from the pattern?
77-
if c == lastChar {
78-
79-
// There is a possible match. Do a brute-force search backwards.
80-
if let k = backwards() { return k }
81-
82-
// If no match, we can only safely skip one character ahead.
83-
i = i.successor()
84-
} else {
85-
// The characters are not equal, so skip ahead. The amount to skip is
86-
// determined by the skip table. If the character is not present in the
87-
// pattern, we can skip ahead by the full pattern length. However, if
88-
// the character *is* present in the pattern, there may be a match up
89-
// ahead and we can't skip as far.
90-
i = i.advancedBy(skipTable[c] ?? patternLength)
91-
}
74+
let c = self[i]
75+
76+
// Does the current character match the last character from the pattern?
77+
if c == lastChar {
78+
79+
// There is a possible match. Do a brute-force search backwards.
80+
if let k = backwards() { return k }
81+
82+
// If no match, we can only safely skip one character ahead.
83+
i = index(after: i)
84+
} else {
85+
// The characters are not equal, so skip ahead. The amount to skip is
86+
// determined by the skip table. If the character is not present in the
87+
// pattern, we can skip ahead by the full pattern length. However, if
88+
// the character *is* present in the pattern, there may be a match up
89+
// ahead and we can't skip as far.
90+
i = index(i, offsetBy: skipTable[c] ?? patternLength)
91+
}
9292
}
9393
return nil
94-
}
94+
}
9595
}
96+
9697
```
9798

9899
The algorithm works as follows. You line up the search pattern with the source string and see what character from the string matches the *last* character of the search pattern:
@@ -149,34 +150,42 @@ Here's an implementation of the Boyer-Moore-Horspool algorithm:
149150

150151
```swift
151152
extension String {
152-
public func indexOf(pattern: String) -> String.Index? {
153+
func indexOf(pattern: String) -> String.Index? {
153154
let patternLength = pattern.characters.count
154155
assert(patternLength > 0)
155156
assert(patternLength <= self.characters.count)
156157

157158
var skipTable = [Character: Int]()
158-
for (i, c) in pattern.characters.dropLast().enumerate() {
159-
skipTable[c] = patternLength - i - 1
159+
for (i, c) in pattern.characters.enumerated() {
160+
skipTable[c] = patternLength - i - 1
160161
}
161162

162-
var index = self.startIndex.advancedBy(patternLength - 1)
163-
164-
while index < self.endIndex {
165-
var i = index
166-
var p = pattern.endIndex.predecessor()
167-
168-
while self[i] == pattern[p] {
169-
if p == pattern.startIndex { return i }
170-
i = i.predecessor()
171-
p = p.predecessor()
172-
}
163+
let p = pattern.index(before: pattern.endIndex)
164+
let lastChar = pattern[p]
165+
var i = self.index(startIndex, offsetBy: patternLength - 1)
173166

174-
let advance = skipTable[self[index]] ?? patternLength
175-
index = index.advancedBy(advance)
167+
func backwards() -> String.Index? {
168+
var q = p
169+
var j = i
170+
while q > pattern.startIndex {
171+
j = index(before: j)
172+
q = index(before: q)
173+
if self[j] != pattern[q] { return nil }
174+
}
175+
return j
176176
}
177177

178+
while i < self.endIndex {
179+
let c = self[i]
180+
if c == lastChar {
181+
if let k = backwards() { return k }
182+
i = index(after: i)
183+
} else {
184+
i = index(i, offsetBy: skipTable[c] ?? patternLength)
185+
}
186+
}
178187
return nil
179-
}
188+
}
180189
}
181190
```
182191

0 commit comments

Comments
 (0)