|
|
package service |
|
|
|
|
|
import ( |
|
|
"bytes" |
|
|
"fmt" |
|
|
"hash/fnv" |
|
|
"sort" |
|
|
"strings" |
|
|
"sync" |
|
|
|
|
|
goahocorasick "github.com/anknown/ahocorasick" |
|
|
) |
|
|
|
|
|
func SundaySearch(text string, pattern string) bool { |
|
|
|
|
|
offset := make(map[rune]int) |
|
|
for i, c := range pattern { |
|
|
offset[c] = len(pattern) - i |
|
|
} |
|
|
|
|
|
|
|
|
n, m := len(text), len(pattern) |
|
|
|
|
|
|
|
|
for i := 0; i <= n-m; { |
|
|
|
|
|
j := 0 |
|
|
for j < m && text[i+j] == pattern[j] { |
|
|
j++ |
|
|
} |
|
|
|
|
|
if j == m { |
|
|
return true |
|
|
} |
|
|
|
|
|
|
|
|
if i+m < n { |
|
|
next := rune(text[i+m]) |
|
|
if val, ok := offset[next]; ok { |
|
|
i += val |
|
|
} else { |
|
|
i += len(pattern) + 1 |
|
|
} |
|
|
} else { |
|
|
break |
|
|
} |
|
|
} |
|
|
return false |
|
|
} |
|
|
|
|
|
func RemoveDuplicate(s []string) []string { |
|
|
result := make([]string, 0, len(s)) |
|
|
temp := map[string]struct{}{} |
|
|
for _, item := range s { |
|
|
if _, ok := temp[item]; !ok { |
|
|
temp[item] = struct{}{} |
|
|
result = append(result, item) |
|
|
} |
|
|
} |
|
|
return result |
|
|
} |
|
|
|
|
|
func InitAc(dict []string) *goahocorasick.Machine { |
|
|
m := new(goahocorasick.Machine) |
|
|
runes := readRunes(dict) |
|
|
if err := m.Build(runes); err != nil { |
|
|
fmt.Println(err) |
|
|
return nil |
|
|
} |
|
|
return m |
|
|
} |
|
|
|
|
|
var acCache sync.Map |
|
|
|
|
|
func acKey(dict []string) string { |
|
|
if len(dict) == 0 { |
|
|
return "" |
|
|
} |
|
|
normalized := make([]string, 0, len(dict)) |
|
|
for _, w := range dict { |
|
|
w = strings.ToLower(strings.TrimSpace(w)) |
|
|
if w != "" { |
|
|
normalized = append(normalized, w) |
|
|
} |
|
|
} |
|
|
if len(normalized) == 0 { |
|
|
return "" |
|
|
} |
|
|
sort.Strings(normalized) |
|
|
hasher := fnv.New64a() |
|
|
for _, w := range normalized { |
|
|
hasher.Write([]byte{0}) |
|
|
hasher.Write([]byte(w)) |
|
|
} |
|
|
return fmt.Sprintf("%x", hasher.Sum64()) |
|
|
} |
|
|
|
|
|
func getOrBuildAC(dict []string) *goahocorasick.Machine { |
|
|
key := acKey(dict) |
|
|
if key == "" { |
|
|
return nil |
|
|
} |
|
|
if v, ok := acCache.Load(key); ok { |
|
|
if m, ok2 := v.(*goahocorasick.Machine); ok2 { |
|
|
return m |
|
|
} |
|
|
} |
|
|
m := InitAc(dict) |
|
|
if m == nil { |
|
|
return nil |
|
|
} |
|
|
if actual, loaded := acCache.LoadOrStore(key, m); loaded { |
|
|
if cached, ok := actual.(*goahocorasick.Machine); ok { |
|
|
return cached |
|
|
} |
|
|
} |
|
|
return m |
|
|
} |
|
|
|
|
|
func readRunes(dict []string) [][]rune { |
|
|
var runes [][]rune |
|
|
|
|
|
for _, word := range dict { |
|
|
word = strings.ToLower(word) |
|
|
l := bytes.TrimSpace([]byte(word)) |
|
|
runes = append(runes, bytes.Runes(l)) |
|
|
} |
|
|
|
|
|
return runes |
|
|
} |
|
|
|
|
|
func AcSearch(findText string, dict []string, stopImmediately bool) (bool, []string) { |
|
|
if len(dict) == 0 { |
|
|
return false, nil |
|
|
} |
|
|
if len(findText) == 0 { |
|
|
return false, nil |
|
|
} |
|
|
m := getOrBuildAC(dict) |
|
|
if m == nil { |
|
|
return false, nil |
|
|
} |
|
|
hits := m.MultiPatternSearch([]rune(findText), stopImmediately) |
|
|
if len(hits) > 0 { |
|
|
words := make([]string, 0) |
|
|
for _, hit := range hits { |
|
|
words = append(words, string(hit.Word)) |
|
|
} |
|
|
return true, words |
|
|
} |
|
|
return false, nil |
|
|
} |
|
|
|