Skip to content

Commit 654fcb5

Browse files
authored
add boyer moore string search (TheAlgorithms#388)
1 parent 3e747fc commit 654fcb5

File tree

3 files changed

+82
-10
lines changed

3 files changed

+82
-10
lines changed

strings/search/boyermoore.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package search
2+
3+
// Implementation of boyer moore string search
4+
// O(l) where l=len(text)
5+
func BoyerMoore(text string, pattern string) []int {
6+
var positions []int
7+
8+
l := len(text)
9+
n := len(pattern)
10+
11+
// using booyer moore horspool modification
12+
// O(n) space instead of O(n**2)
13+
bcr := make(map[byte]int)
14+
for i := 0; i < n-1; i++ {
15+
bcr[pattern[i]] = n - i - 1
16+
}
17+
18+
// Apostolico–Giancarlo modification
19+
// allow to skip patterns that we know matches
20+
// let us do O(l) instead of O(ln)
21+
skips := make(map[int]int)
22+
for _, s := range bcr {
23+
i := 0
24+
for ; i < n-s; i++ {
25+
if pattern[n-1-i] != pattern[n-1-s-i] {
26+
break
27+
}
28+
}
29+
skips[s] = i
30+
}
31+
32+
skip := 0
33+
jump := n
34+
for i := 0; i < l-n+1; {
35+
skip = skips[jump]
36+
for k := n - 1; k > -1; k-- {
37+
if text[i+k] != pattern[k] {
38+
jump, ok := bcr[text[i+k]]
39+
if !ok {
40+
jump = n
41+
}
42+
i += jump
43+
break
44+
}
45+
if k == n-jump {
46+
k -= skip
47+
}
48+
if k == 0 {
49+
positions = append(positions, i)
50+
jump = 1
51+
i += jump
52+
}
53+
}
54+
}
55+
56+
return positions
57+
}

strings/search/patternsearch.go renamed to strings/search/naive.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
1-
/*
2-
This algorithm tries to search the given pattern in the given text.
3-
If pattern is found from index position i in the text,
4-
it is added to positions.
5-
6-
Time Complexity : O(n*m)
7-
n = length of text
8-
m = length of pattern
9-
*/
10-
111
package search
122

3+
// Implementation of naive string search
4+
// O(n*m) where n=len(txt) and m=len(pattern)
135
func Naive(text string, pattern string) []int {
146
var positions []int
157
for i := 0; i <= len(text)-len(pattern); i++ {

strings/search/patternsearch_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ var testCases = []struct {
3535
"XYZ",
3636
[]int(nil),
3737
},
38+
{
39+
"worse case 1",
40+
"AAAAAAAAAA",
41+
"AAA",
42+
[]int{0, 1, 2, 3, 4, 5, 6, 7},
43+
},
44+
{
45+
"worse case 2",
46+
"NANANANANANANANANA",
47+
"NANANA",
48+
[]int{0, 2, 4, 6, 8, 10, 12},
49+
},
3850
}
3951

4052
func TestNaive(t *testing.T) {
@@ -47,3 +59,14 @@ func TestNaive(t *testing.T) {
4759
})
4860
}
4961
}
62+
63+
func TestBooyerMoore(t *testing.T) {
64+
for _, tc := range testCases {
65+
t.Run(tc.name, func(t *testing.T) {
66+
actual := BoyerMoore(tc.input, tc.pattern)
67+
if !reflect.DeepEqual(actual, tc.expected) {
68+
t.Errorf("Expected matches for pattern '%s' for string '%s' are: %v, but actual matches are: %v", tc.pattern, tc.input, tc.expected, actual)
69+
}
70+
})
71+
}
72+
}

0 commit comments

Comments
 (0)