Skip to content

Commit

Permalink
geeksforgeeks: add auto-complete feature
Browse files Browse the repository at this point in the history
  • Loading branch information
minizilla committed Dec 16, 2023
1 parent e49a70c commit 4c60fa7
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 0 deletions.
14 changes: 14 additions & 0 deletions geeksforgeeks/auto-complete-feature/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Auto-complete feature

We are given a Trie with a set of strings stored in it.
Now the user types in a prefix of his search query,
we need to give him all recommendations to auto-complete his query based on the strings stored in the Trie.

## Copyright Notice

This problem is based on [content](https://www.geeksforgeeks.org/auto-complete-feature-using-trie/)
from [GeeksforGeeks](https://www.geeksforgeeks.org)
written by Hemang Sarkar
and subject to [GeeksforGeeks copyright](https://www.geeksforgeeks.org/legal/copyright-information/).
The original content from GeeksforGeeks and any modifications made here are attributed to GeeksforGeeks contributors,
and this work is shared under [CC BY-SA 4.0](../LICENSE).
106 changes: 106 additions & 0 deletions geeksforgeeks/auto-complete-feature/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package auto_complete_feature

const alphabetSize = 26 + 1

func Solution(q string, dict []string) []string {
if q == "" {
return nil
}

root := newTrieNode()
for _, word := range dict {
root.insert(word)
}

return root.autoComplete(q)
}

type trieNode struct {
childs []*trieNode
isWord bool
isLeaf bool
}

func newTrieNode() *trieNode {
return &trieNode{
childs: make([]*trieNode, alphabetSize),
}
}

func (node *trieNode) insert(s string) {
if len(s) == 0 {
node.isLeaf = true
node.isWord = true
return
}

index := ctoi(rune(s[0]))
child := node.childs[index]
if child == nil {
child = newTrieNode()
node.childs[index] = child
}

child.isLeaf = false
child.insert(s[1:])
}

func (node *trieNode) autoComplete(s string) []string {
next := node
for _, r := range s {
next = next.childs[ctoi(r)]
if next == nil {
return nil
}
}

if next == nil {
return nil
}

return next.append(s)
}

func (node *trieNode) append(s string) []string {
var out []string
if node.isWord {
out = append(out, s)
}

for i, child := range node.childs {
if child == nil {
continue
}

prefix := s + string(itoc(rune(i)))

if child.isLeaf {
out = append(out, prefix)
continue
}

for _, word := range child.append(prefix) {
out = append(out, word)
}
}

return out
}

func itoc(i rune) rune {
switch i {
case 26:
return ' '
default:
return i + 'a'
}
}

func ctoi(c rune) rune {
switch c {
case ' ':
return 26
default:
return c - 'a'
}
}
57 changes: 57 additions & 0 deletions geeksforgeeks/auto-complete-feature/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package auto_complete_feature_test

import (
"testing"

sut "github.com/minizilla/minmax/geeksforgeeks/auto-complete-feature"
"github.com/minizilla/testr"
)

func TestAutoCompleteFeature(t *testing.T) {
dict := []string{
"i am iron man",
"i am iron man endgame",
"i am iron man meme",
"i am venom",
"i am venom sound",
"i am venom morbius",
"i am batman",
}

tests := map[string]struct {
q string
res []string
}{
"empty": {"", nil},
"can't complete": {"i am man", nil},
"incomplete": {
"i am iro",
[]string{
"i am iron man",
"i am iron man endgame",
"i am iron man meme",
},
},
"incomplete with space": {
"i am venom ",
[]string{
"i am venom morbius",
"i am venom sound",
},
},
"complete": {
"i am batman",
[]string{
"i am batman",
},
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
assert := testr.New(t)
res := sut.Solution(tc.q, dict)
assert.Equal(res, tc.res)
})
}
}

0 comments on commit 4c60fa7

Please sign in to comment.