glider/rule/internal/trie/trie.go
2020-12-04 18:49:11 +08:00

129 lines
2.0 KiB
Go

// Static trie
package trie
import (
"strings"
)
type cast map[rune]*next
type next struct {
*node
str *string
}
type node struct {
c cast
end bool
}
type Trie struct {
root *node
}
func newNode() *node {
return &node{
c: cast{},
end: false,
}
}
func New(dict []string) (trie *Trie) {
var t Trie
var ok bool
var p *node
t.root = newNode()
for _, d := range dict {
p = t.root
for i, r := range d {
_, ok = p.c[r]
if !ok {
n := next{
node: newNode(),
str: nil,
}
p.c[r] = &n
}
p = p.c[r].node
if i == len(d)-1 {
p.end = true
}
}
}
//make jump
makeJump(t.root)
return &t
}
func fastJump(from *next, to *next, str *string) {
from.str = str
from.node = to.node
}
func _makeJump(cur *next, from *next, builder *strings.Builder) {
var fork bool
if cur.node.end || len(cur.node.c) > 1 {
if builder.Len() > 1 {
s := builder.String()
fastJump(from, cur, &s)
}
fork = true
}
for k := range cur.node.c {
child := cur.node.c[k]
if fork {
from = child
builder = new(strings.Builder)
}
builder.WriteRune(k)
_makeJump(child, from, builder)
}
}
func makeJump(root *node) {
//DFS
for k := range root.c {
builder := new(strings.Builder)
builder.WriteRune(k)
_makeJump(root.c[k], root.c[k], builder)
}
}
func (t *Trie) Match(str string) (prefix string) {
var builder strings.Builder
var runes = []rune(str)
var length = len(runes)
p := t.root
for i := 0; i < length; i++ {
r := runes[i]
tmp, ok := p.c[r]
if !ok {
return
}
if tmp.str == nil {
builder.WriteRune(r)
} else {
if lenTmp := len(*tmp.str); builder.Len()+lenTmp <= length {
if string(runes[i:lenTmp+i]) == *tmp.str {
builder.WriteString(*tmp.str)
i += len(*tmp.str) - 1
} else {
break
}
}
}
if tmp.node.end {
if builder.Len() <= length {
prefix = builder.String()
if len(prefix) == length {
break
}
} else {
break
}
}
p = tmp.node
}
return
}