Skip to content

Commit 41fa294

Browse files
authored
Add: kmp(Knuth-Morris-Pratt) algorithm (#586)
1 parent c027418 commit 41fa294

File tree

3 files changed

+81
-146
lines changed

3 files changed

+81
-146
lines changed

README.md

+1-7
Original file line numberDiff line numberDiff line change
@@ -435,13 +435,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
435435

436436
##### Functions:
437437

438-
1. [`Kmp`](./strings/kmp/kmp.go#L70): Kmp Function kmp performing the Knuth-Morris-Pratt algorithm. Prints whether the word/pattern was found and on what position in the text or not. m - current match in text, i - current character in w, c - amount of comparisons.
439-
440-
---
441-
##### Types
442-
443-
1. [`Result`](./strings/kmp/kmp.go#L15): No description provided.
444-
438+
1. [`Kmp`](./strings/kmp/kmp.go#L4): Kmp Function kmp performing the Knuth-Morris-Pratt algorithm.
445439

446440
---
447441
</details><details>

strings/kmp/kmp.go

+32-100
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,49 @@
11
package kmp
22

3-
import (
4-
"fmt"
5-
)
6-
7-
// User defined.
8-
// Set to true to read input from two command line arguments
9-
// Set to false to read input from two files "pattern.txt" and "text.txt"
10-
11-
// const isTakingInputFromCommandLine bool = true
12-
13-
const notFoundPosition int = -1
14-
15-
type Result struct {
16-
resultPosition int
17-
numberOfComparison int
18-
}
19-
20-
// Implementation of Knuth-Morris-Pratt algorithm (Prefix based approach).
21-
// Requires either a two command line arguments separated by a single space,
22-
// or two files in the same folder: "pattern.txt" containing the string to
23-
// be searched for, "text.txt" containing the text to be searched in.
24-
// func main() {
25-
// var text string
26-
// var word string
27-
28-
// if isTakingInputFromCommandLine { // case of command line input
29-
// args := os.Args
30-
// if len(args) <= 2 {
31-
// log.Fatal("Not enough arguments. Two string arguments separated by spaces are required!")
32-
// }
33-
// word = args[1]
34-
// text = args[2]
35-
// for i := 3; i < len(args); i++ {
36-
// text = text + " " + args[i]
37-
// }
38-
// } else { // case of file input
39-
// patFile, err := ioutil.ReadFile("../pattern.txt")
40-
// if err != nil {
41-
// log.Fatal(err)
42-
// }
43-
// textFile, err := ioutil.ReadFile("../text.txt")
44-
// if err != nil {
45-
// log.Fatal(err)
46-
// }
47-
// text = string(textFile)
48-
// word = string(patFile)
49-
// }
3+
// Kmp Function kmp performing the Knuth-Morris-Pratt algorithm.
4+
func Kmp(word, text string, patternTable []int) []int {
5+
if len(word) > len(text) {
6+
return nil
7+
}
508

51-
// if len(word) > len(text) {
52-
// log.Fatal("Pattern is longer than text!")
53-
// }
54-
// fmt.Printf("\nRunning: Knuth-Morris-Pratt algorithm.\n\n")
55-
// fmt.Printf("Search word (%d chars long): %q.\n", len(word), word)
56-
// fmt.Printf("Text (%d chars long): %q.\n\n", len(text), text)
9+
var (
10+
i, j int
11+
matches []int
12+
)
13+
for i+j < len(text) {
5714

58-
// r := kmp(text, word)
59-
// if r.resultPosition == notFoundPosition {
60-
// fmt.Printf("\n\nWord was not found.\n%d comparisons were done.", r.numberOfComparison)
61-
// } else {
62-
// fmt.Printf("\n\nWord %q was found at position %d in %q. \n%d comparisons were done.", word,
63-
// r.resultPosition, text, r.numberOfComparison)
64-
// }
65-
// }
15+
if word[j] == text[i+j] {
16+
j++
17+
if j == len(word) {
18+
matches = append(matches, i)
6619

67-
// Kmp Function kmp performing the Knuth-Morris-Pratt algorithm.
68-
// Prints whether the word/pattern was found and on what position in the text or not.
69-
// m - current match in text, i - current character in w, c - amount of comparisons.
70-
func Kmp(text string, word string) Result {
71-
m, i, c := 0, 0, 0
72-
t := kmpTable(word)
73-
for m+i < len(text) {
74-
fmt.Printf("\n comparing characters %c %c at positions %d %d", text[m+i], word[i], m+i, i)
75-
c++
76-
if word[i] == text[m+i] {
77-
fmt.Printf(" - match")
78-
if i == len(word)-1 {
79-
return Result{
80-
m, c,
81-
}
20+
i = i + j
21+
j = 0
8222
}
83-
i++
8423
} else {
85-
m = m + i - t[i]
86-
if t[i] > -1 {
87-
i = t[i]
24+
i = i + j - patternTable[j]
25+
if patternTable[j] > -1 {
26+
j = patternTable[j]
8827
} else {
89-
i = 0
28+
j = 0
9029
}
9130
}
9231
}
93-
return Result{notFoundPosition,
94-
c,
95-
}
32+
return matches
9633
}
9734

98-
// Table building algorithm.
99-
// Takes word to be analyzed and table to be filled.
100-
func kmpTable(word string) (t []int) {
101-
t = make([]int, len(word))
102-
pos, cnd := 2, 0
103-
t[0], t[1] = -1, 0
104-
for pos < len(word) {
105-
if word[pos-1] == word[cnd] {
106-
cnd++
107-
t[pos] = cnd
108-
pos++
109-
} else if cnd > 0 {
110-
cnd = t[cnd]
111-
} else {
112-
t[pos] = 0
113-
pos++
35+
// table building for kmp algorithm.
36+
func table(w string) []int {
37+
var (
38+
t []int = []int{-1}
39+
k int
40+
)
41+
for j := 1; j < len(w); j++ {
42+
k = j - 1
43+
for w[0:k] != w[j-k:j] && k > 0 {
44+
k--
11445
}
46+
t = append(t, k)
11547
}
11648
return t
11749
}

strings/kmp/kmp_test.go

+48-39
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,57 @@ import (
55
"testing"
66
)
77

8-
var testCases = []struct {
9-
name string
10-
word string
11-
text string
12-
expected Result
13-
}{
14-
{
15-
"String comparison on single pattern match",
16-
"announce",
17-
"CPM_annual_conference_announce",
18-
Result{
19-
22,
20-
32,
21-
},
22-
},
23-
{
24-
"String comparison on multiple pattern match",
25-
"AABA",
26-
"AABAACAADAABAABA",
27-
Result{
28-
0,
29-
4,
30-
},
31-
},
32-
{
33-
"String comparison with not found pattern",
34-
"AABC",
35-
"AABAACAADAABAABA",
36-
Result{
37-
-1,
38-
23,
8+
func TestKmp(t *testing.T) {
9+
type args struct {
10+
word string
11+
text string
12+
patternTable []int
13+
}
14+
tests := []struct {
15+
name string
16+
args args
17+
want []int
18+
}{
19+
{
20+
name: "test1",
21+
args: args{
22+
word: "ab",
23+
text: "ababacaab",
24+
patternTable: table("ababacaab"),
25+
},
26+
want: []int{0, 2, 7},
3927
},
40-
},
28+
}
29+
for _, tt := range tests {
30+
t.Run(tt.name, func(t *testing.T) {
31+
if got := Kmp(tt.args.word, tt.args.text, tt.args.patternTable); !reflect.DeepEqual(got, tt.want) {
32+
t.Errorf("Kmp() = %v, want %v", got, tt.want)
33+
}
34+
})
35+
}
4136
}
4237

43-
func TestKMP(t *testing.T) {
44-
for _, tc := range testCases {
45-
t.Run(tc.name, func(t *testing.T) {
46-
actual := Kmp(tc.text, tc.word)
47-
if !reflect.DeepEqual(actual, tc.expected) {
48-
t.Errorf("Expected matches for pattern '%s' for string '%s' are: %v steps at position %v, but actual matches are: %v steps at position %v",
49-
tc.word, tc.text, tc.expected.numberOfComparison, tc.expected.resultPosition, actual.numberOfComparison, actual.resultPosition)
38+
func TestTable(t *testing.T) {
39+
type args struct {
40+
w string
41+
}
42+
tests := []struct {
43+
name string
44+
args args
45+
want []int
46+
}{
47+
{
48+
name: "test1",
49+
args: args{
50+
w: "ababacaab",
51+
},
52+
want: []int{-1, 0, 0, 1, 2, 3, 0, 1, 1},
53+
},
54+
}
55+
for _, tt := range tests {
56+
t.Run(tt.name, func(t *testing.T) {
57+
if got := table(tt.args.w); !reflect.DeepEqual(got, tt.want) {
58+
t.Errorf("Table() = %v, want %v", got, tt.want)
5059
}
5160
})
5261
}

0 commit comments

Comments
 (0)