Repository: vaskoz/dailycodingproblem-go (go)

Issue:
Day 11
Good morning! Here's your coding interview problem for today.

This problem was asked by Twitter.

Implement an autocomplete system. That is, given a query string s and a set of all possible query strings, return all strings in the set that have s as a prefix.

For example, given the query string de and the set of strings [dog, deer, deal], return [deer, deal].

Hint: Try preprocessing the dictionary into a more efficient data structure to speed up queries.

Pull request description:
day 11 solution using a trie
Resolves #23 

Patch:
diff --git a/README.md b/README.md
index ccf6df4..78ccbc6 100644
--- a/README.md
+++ b/README.md
@@ -20,3 +20,4 @@ problems from
 * [Day 8](https://github.com/vaskoz/dailycodingproblem-go/issues/17)
 * [Day 9](https://github.com/vaskoz/dailycodingproblem-go/issues/19)
 * [Day 10](https://github.com/vaskoz/dailycodingproblem-go/issues/21)
+* [Day 11](https://github.com/vaskoz/dailycodingproblem-go/issues/23)
diff --git a/day11/problem.go b/day11/problem.go
new file mode 100644
index 0000000..e8b2d84
--- /dev/null
+++ b/day11/problem.go
@@ -0,0 +1,53 @@
+package day11
+
+// Trie represents a prefix tree.
+type Trie struct {
+	letters   map[rune]*Trie
+	wordCount int
+}
+
+// NewTrie constructs a new trie with the given words.
+func NewTrie(words []string) *Trie {
+	root := &Trie{letters: make(map[rune]*Trie)}
+	for _, word := range words {
+		current := root
+		for _, letter := range word {
+			if n, found := current.letters[letter]; !found {
+				current.letters[letter] = &Trie{letters: make(map[rune]*Trie)}
+				current = current.letters[letter]
+			} else {
+				current = n
+			}
+		}
+		current.wordCount++
+	}
+	return root
+}
+
+// Match returns a list of words that match the given prefix.
+func (t *Trie) Match(prefix string) []string {
+	var result []string
+	current := t
+	for _, r := range prefix {
+		n, found := current.letters[r]
+		if !found {
+			return result // prefix not found anywhere
+		}
+		current = n
+	}
+	return append(result, dfs(current, prefix)...)
+}
+
+func dfs(trie *Trie, prefix string) []string {
+	var words []string
+	if len(trie.letters) == 0 {
+		return []string{prefix}
+	}
+	for letter, node := range trie.letters {
+		words = append(words, dfs(node, prefix+string(letter))...)
+	}
+	if trie.wordCount > 0 {
+		words = append(words, prefix)
+	}
+	return words
+}


Test patch:
diff --git a/day11/problem_test.go b/day11/problem_test.go
new file mode 100644
index 0000000..1adb21d
--- /dev/null
+++ b/day11/problem_test.go
@@ -0,0 +1,42 @@
+package day11
+
+import (
+	"reflect"
+	"sort"
+	"testing"
+)
+
+var testcases = []struct {
+	input    []string
+	query    string
+	expected []string
+}{
+	{[]string{"dog", "deer", "deal"}, "de", []string{"deal", "deer"}},
+	{[]string{"dog", "deer", "deal"}, "a", nil},
+	{[]string{"dog", "deer", "doggone"}, "dog", []string{"dog", "doggone"}},
+	{[]string{"abc", "abcd", "adec", "bde"}, "bde", []string{"bde"}},
+}
+
+func TestTrie(t *testing.T) {
+	t.Parallel()
+	for _, tc := range testcases {
+		trie := NewTrie(tc.input)
+		result := trie.Match(tc.query)
+		sort.Strings(result)
+		if !reflect.DeepEqual(tc.expected, result) {
+			t.Errorf("Expected %v by got %v for query %v", tc.expected, result, tc.query)
+		}
+	}
+}
+
+func BenchmarkTrie(b *testing.B) {
+	tries := make([]*Trie, len(testcases))
+	for i := range tries {
+		tries[i] = NewTrie(testcases[i].input)
+	}
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		index := i % len(tries)
+		tries[index].Match(testcases[index].query)
+	}
+}