github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/ac/aho_corasick.go (about) 1 package ac 2 3 import ( 4 "github.com/qiuhoude/go-web/algorithm/datastructures/queue" 5 ) 6 7 // 节点 8 type acNode struct { 9 children map[rune]*acNode // 孩子节点 10 isEnd bool // 结束标识符 11 fail *acNode // 失败节点 12 length int 13 } 14 15 func newAcNode() *acNode { 16 return &acNode{ 17 children: make(map[rune]*acNode), 18 } 19 } 20 21 // Ac自动机 Aho-Corasick 22 type AC struct { 23 root *acNode // 根节点 24 size int // 自动机中词的数量 25 } 26 27 func NewAc() *AC { 28 return &AC{ 29 root: newAcNode(), 30 size: 0, 31 } 32 } 33 34 // 添加词 35 func (ac *AC) AddWorld(w string) { 36 cur := ac.root 37 aw := []rune(w) 38 for _, v := range aw { 39 n, ok := cur.children[v] 40 if !ok { 41 n = newAcNode() 42 cur.children[v] = n 43 } 44 cur = n 45 } 46 if !cur.isEnd { 47 cur.isEnd = true 48 cur.length = len(aw) //长度 49 ac.size++ 50 } 51 } 52 53 func (ac *AC) AddWorlds(worlds []string) { 54 for _, v := range worlds { 55 ac.AddWorld(v) 56 } 57 } 58 59 // 构建失败节点 60 func (ac *AC) BuildFailurePointer() { 61 que := queue.NewLinkedQueue() 62 ac.root.fail = nil // 根节点的失败节点是nil 63 que.Enqueue(ac.root) 64 for !que.IsEmpty() { 65 cur := que.Dequeue().(*acNode) 66 if cur == ac.root { // 取出来的是root,就将子节点的的fail节点指向root 67 for _, v := range cur.children { 68 v.fail = cur 69 que.Enqueue(v) 70 } 71 } else { 72 for k, v := range cur.children { 73 failTo := cur.fail 74 for failTo != nil { 75 if node, ok := failTo.children[k]; ok { 76 v.fail = node 77 break 78 } else { 79 failTo = failTo.fail 80 } 81 } 82 if failTo == nil { 83 v.fail = ac.root 84 } 85 que.Enqueue(v) 86 } 87 } 88 } 89 } 90 91 // 匹配 92 func (ac *AC) Match(s string, f func(start, end int)) { 93 text := []rune(s) 94 textLen := len(text) 95 p := ac.root 96 for i := 0; i < textLen; i++ { 97 c := text[i] 98 var ok bool 99 for _, ok = p.children[c]; !ok && p != ac.root; { 100 // 不匹配, 一直往前找,直到匹配或到root节点 101 p = p.fail 102 _, ok = p.children[c] 103 } 104 if p, ok = p.children[c]; !ok { 105 p = ac.root 106 continue 107 } 108 cur := p 109 for cur != ac.root { 110 // 此处加个for 属于匹配更多的屏蔽词 111 if cur.isEnd { 112 // 找到了匹配的位置 113 f(i-cur.length+1, i) 114 } 115 cur = cur.fail 116 } 117 } 118 } 119 120 // 删除词 121 func (ac *AC) Remove(w string) bool { 122 cur := ac.root 123 var stack []*acNode 124 runeArr := []rune(w) 125 for _, v := range runeArr { 126 127 n, ok := cur.children[v] 128 if !ok { // 没有找到该单词不用删除 129 return false 130 } 131 stack = append(stack, n) 132 cur = n 133 } 134 ac.size-- 135 // 结尾标识改掉 136 cur.isEnd = false 137 if len(cur.children) == 1 { // 只有自己一个字符 没有后续的 就可以进行移除操作 138 for i := len(stack) - 1; i >= 0; i-- { 139 c := runeArr[i] 140 n := stack[i] 141 delete(n.children, c) 142 if cur.isEnd { 143 break 144 } 145 } 146 } 147 return true 148 } 149 150 func (ac *AC) Contains(w string) bool { 151 cur := ac.root 152 for _, v := range []rune(w) { 153 154 n, ok := cur.children[v] 155 if !ok { 156 return false 157 } 158 cur = n 159 } 160 if !cur.isEnd { // 不是结束就返回nil 161 return false 162 } 163 return true 164 }