Skip to content

Commit 7711637

Browse files
committed
Put brute-force string search in its own folder
1 parent 0a26492 commit 7711637

File tree

8 files changed

+130
-34
lines changed

8 files changed

+130
-34
lines changed

Boyer-Moore/BoyerMoore.playground/Contents.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ extension String {
3939
}
4040
}
4141

42+
43+
4244
// A few simple tests
4345

4446
let s = "Hello, World"
4547
s.indexOf("World") // 7
4648

47-
// Input:
4849
let animals = "🐶🐔🐷🐮🐱"
4950
animals.indexOf("🐮") // 6

Boyer-Moore/README.markdown

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Boyer-Moore String Search
22

3-
How would you go about writing a string search algorithm in pure Swift if you were not allowed to import Foundation and could not use `NSString`'s `rangeOfString()` method?
4-
5-
The goal is to implement an `indexOf(pattern: String)` extension on `String` that returns the `String.Index` of the first occurrence of the search pattern, or `nil` if the pattern could not be found inside the string.
3+
Goal: Write a string search algorithm in pure Swift without importing Foundation or using `NSString`'s `rangeOfString()` method.
4+
5+
In other words, implement an `indexOf(pattern: String)` extension on `String` that returns the `String.Index` of the first occurrence of the search pattern, or `nil` if the pattern could not be found inside the string.
66

77
For example:
88

@@ -22,36 +22,9 @@ animals.indexOf("🐮")
2222
<String.Index?> 6
2323
```
2424

25-
Note: The index of the cow is 6, not 3 as you might expect, because the string uses more storage per character for emoji. The actual value of the `String.Index` is not so important, just that it points at the right character in the string.
26-
27-
First, a brute-force solution:
28-
29-
```swift
30-
extension String {
31-
func indexOf(pattern: String) -> String.Index? {
32-
for i in self.startIndex ..< self.endIndex {
33-
var j = i
34-
var found = true
35-
for p in pattern.startIndex ..< pattern.endIndex {
36-
if j == self.endIndex || self[j] != pattern[p] {
37-
found = false
38-
break
39-
} else {
40-
j = j.successor()
41-
}
42-
}
43-
if found {
44-
return i
45-
}
46-
}
47-
return nil
48-
}
49-
}
50-
```
51-
52-
This looks at each character in the source string in turn. If the character equals the first character of the search pattern, then the inner loop checks whether the rest of the pattern matches. If no match is found, the outer loop continues where it left off. This repeats until a complete match is found or the end of the source string is reached.
25+
> **Note:** The index of the cow is 6, not 3 as you might expect, because the string uses more storage per character for emoji. The actual value of the `String.Index` is not so important, just that it points at the right character in the string.
5326
54-
The brute-force approach works OK, but it's not very efficient (or pretty). As it turns out, you don't need to look at *every* character from the source string -- you can often skip ahead multiple characters.
27+
The [brute-force approach](../Brute-Force Search Search/) works OK, but it's not very efficient, especially on large chunks of text. As it turns out, you don't need to look at *every* character from the source string -- you can often skip ahead multiple characters.
5528

5629
That skip-ahead algorithm is called [Boyer-Moore](https://en.wikipedia.org/wiki/Boyer–Moore_string_search_algorithm) and it has been around for a long time. It is considered the benchmark for all string search algorithms.
5730

@@ -164,4 +137,4 @@ A caveat: If the search pattern consists of only a few characters, it's faster t
164137

165138
Credits: This code is based on the article ["Faster String Searches" by Costas Menico](http://www.drdobbs.com/database/faster-string-searches/184408171) from Dr Dobb's magazine, July 1989 -- Yes, 1989! Sometimes it's useful to keep those old magazines around.
166139

167-
*Written by Matthijs Hollemans*
140+
*Written for Swift Algorithm Club by Matthijs Hollemans*
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//: Playground - noun: a place where people can play
2+
3+
extension String {
4+
func indexOf(pattern: String) -> String.Index? {
5+
for i in self.startIndex ..< self.endIndex {
6+
var j = i
7+
var found = true
8+
for p in pattern.startIndex ..< pattern.endIndex {
9+
if j == self.endIndex || self[j] != pattern[p] {
10+
found = false
11+
break
12+
} else {
13+
j = j.successor()
14+
}
15+
}
16+
if found {
17+
return i
18+
}
19+
}
20+
return nil
21+
}
22+
}
23+
24+
25+
26+
// A few simple tests
27+
28+
let s = "Hello, World"
29+
s.indexOf("World") // 7
30+
31+
let animals = "🐶🐔🐷🐮🐱"
32+
animals.indexOf("🐮") // 6
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='osx'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Timeline
3+
version = "3.0">
4+
<TimelineItems>
5+
</TimelineItems>
6+
</Timeline>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
Brute-force string search
3+
*/
4+
extension String {
5+
func indexOf(pattern: String) -> String.Index? {
6+
for i in self.startIndex ..< self.endIndex {
7+
var j = i
8+
var found = true
9+
for p in pattern.startIndex ..< pattern.endIndex {
10+
if j == self.endIndex || self[j] != pattern[p] {
11+
found = false
12+
break
13+
} else {
14+
j = j.successor()
15+
}
16+
}
17+
if found {
18+
return i
19+
}
20+
}
21+
return nil
22+
}
23+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Brute-Force String Search
2+
3+
How would you go about writing a string search algorithm in pure Swift if you were not allowed to import Foundation and could not use `NSString`'s `rangeOfString()` method?
4+
5+
The goal is to implement an `indexOf(pattern: String)` extension on `String` that returns the `String.Index` of the first occurrence of the search pattern, or `nil` if the pattern could not be found inside the string.
6+
7+
For example:
8+
9+
```swift
10+
// Input:
11+
let s = "Hello, World"
12+
s.indexOf("World")
13+
14+
// Output:
15+
<String.Index?> 7
16+
17+
// Input:
18+
let animals = "🐶🐔🐷🐮🐱"
19+
animals.indexOf("🐮")
20+
21+
// Output:
22+
<String.Index?> 6
23+
```
24+
25+
> **Note:** The index of the cow is 6, not 3 as you might expect, because the string uses more storage per character for emoji. The actual value of the `String.Index` is not so important, just that it points at the right character in the string.
26+
27+
Here is a brute-force solution:
28+
29+
```swift
30+
extension String {
31+
func indexOf(pattern: String) -> String.Index? {
32+
for i in self.startIndex ..< self.endIndex {
33+
var j = i
34+
var found = true
35+
for p in pattern.startIndex ..< pattern.endIndex {
36+
if j == self.endIndex || self[j] != pattern[p] {
37+
found = false
38+
break
39+
} else {
40+
j = j.successor()
41+
}
42+
}
43+
if found {
44+
return i
45+
}
46+
}
47+
return nil
48+
}
49+
}
50+
```
51+
52+
This looks at each character in the source string in turn. If the character equals the first character of the search pattern, then the inner loop checks whether the rest of the pattern matches. If no match is found, the outer loop continues where it left off. This repeats until a complete match is found or the end of the source string is reached.
53+
54+
The brute-force approach works OK, but it's not very efficient (or pretty). It should work fine on small strings, though. For a smarter algorithm that works better with large chunks of text, check out [Boyer-Moore](../Boyer-Moore/) string search.
55+
56+
*Written for Swift Algorithm Club by Matthijs Hollemans*

README.markdown

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ If you're new to algorithms and data structures, here are a few good ones to sta
4747

4848
### String Search
4949

50+
- [Brute-Force String Search](Brute-Force String Search/). A naive method.
5051
- [Boyer-Moore](Boyer-Moore/). A fast method to search for substrings. It skips ahead based on a look-up table, to avoid looking at every character in the text.
5152
- Rabin-Karp
5253

0 commit comments

Comments
 (0)