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  }