这是我参与「第三届青训营 -后端场」笔记创作活动的第4篇笔记。
抖音项目开发过程中,打算实现一个过滤敏感词的Filter组件,所用到的数据结构为字典树(trie)
- 字典树的简单介绍 字典树(又叫单词查找树、TrieTree),是一种树形结构,典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串)。主要思想是利用字符串的公共前缀来节约存储空间。很好地利用了串的公共前缀,节约了存储空间。 它是一种哈希树的变种,常用于,统计,排序,保存大量字符串(但不仅限于字符串),主要实现方法是利用串的公共前缀来减少查询时间,减少了不必要的比较,不仅节约了存储空间,而且检索的效率比哈希表要高。
字典树的建树过程很简单,使用公共前缀(下图即为一颗字典树)
在建好树后,过滤敏感词的过程即为在树上对文本内容进行字符串匹配的过程
- 具体实现(见项目:github.com/uccInf/TikT… )
package utils
import (
"TikTok/constdef"
"fmt"
"unicode/utf8"
)
var invalidWords = []string{"傻逼", "sb", "赌博", "弱智"}
var trie *Trie
type Trie struct {
child map[rune]*Trie
word string
}
func NewTrie() *Trie {
return &Trie{
child: make(map[rune]*Trie),
word: "",
}
}
func (trie *Trie) insert(word string) *Trie {
cur := trie
for _, v := range []rune(word) {
if _, ok := cur.child[v]; !ok {
t := NewTrie()
cur.child[v] = t
}
cur = cur.child[v]
}
cur.word = word
return trie
}
func (trie *Trie) FilterString(word string) string {
cur := trie
for i, v := range []rune(word) {
if _, ok := cur.child[v]; ok {
cur = cur.child[v]
if cur.word != "" {
word = replaceStr(word, constdef.Replace, i+1-utf8.RuneCountInString(cur.word), i)
cur = trie
}
} else {
cur = trie
}
return word
}
func replaceStr(word, replace string, left, right int) string {
str := ""
for i, v := range []rune(word) {
if i >= left && i <= right {
str = str + replace
} else {
str += string(v)
}
}
return str
}
func init() {
trie = NewTrie()
for i := 0; i < len(invalidWords); i++ {
trie.insert(invalidWords[i])
}
}
func GetTrie() *Trie {
return trie
}
测试代码:
package main
import (
"TikTok/utils"
"fmt"
)
func main() {
var t = utils.GetTrie()
fmt.Println(t.FilterString("傻逼,sb, sdf, 尼玛 ,哈哈哈哈哈"))
}