@@ -10,146 +10,146 @@ AC自动机要解决的问题是,在一个文章中,有一些候选字符串
1010
1111为每一个候选串建立一个前缀树,每个树节点都有一个fail指针。头节点fail指针人为规定指向null,第一层节点的fail指针人为规定,指向头节点。建立好前缀树后,宽度优先遍历设置全部的fail指针
1212
13- > 比较绕,看不懂看代码
14-
15- 宽度优先遍历设置fali的指针的过程,如果某个节点的指针指向null,孩子的fail指针指向当前的父亲;如果某个节点的fail指针指向不为空的节点A,A孩子的路径为B,那么A的fali指针有没有指向B的路径,如果有,A孩子的fail指针,指向父亲节点的fail指针指向的B;如果父亲没有指向B的路,再找fail直到为null后,孩子fail指针指向头结点
16-
17-
18-
19- ``` Java
20- package class08 ;
21-
22- import java.util.ArrayList ;
23- import java.util.LinkedList ;
24- import java.util.List ;
25- import java.util.Queue ;
26-
27- public class Code01_AC {
28-
29- // 前缀树的节点
30- public static class Node {
31- // 如果一个node,end为空,不是结尾
32- // 如果end不为空,表示这个点是某个字符串的结尾,end的值就是这个字符串
33- public String end;
34- // 只有在上面的end变量不为空的时候,endUse才有意义
35- // 表示,这个字符串之前有没有加入过答案
36- public boolean endUse;
37- public Node fail;
38- public Node [] nexts;
39- public Node () {
40- endUse = false ;
41- end = null ;
42- fail = null ;
43- // 假设前缀树的节点上的值只是小写字母,有26个指向。经典前缀树
44- nexts = new Node [26 ];
45- }
13+ > 比较绕,可以考虑看代码详细步骤来理解
14+
15+ 宽度优先遍历设置fail的指针的过程,如果某个节点的指针指向null,孩子的fail指针指向当前的父亲;如果某个节点的fail指针指向不为空的节点A,A孩子的路径为B,那么A的fail指针有没有指向B的路径,如果有,A孩子的fail指针,指向父亲节点的fail指针指向的B;如果父亲没有指向B的路,再找fail直到为null后,孩子fail指针指向头结点
16+
17+
18+
19+ ``` Go
20+ package main
21+
22+ import " fmt"
23+
24+ // Node 前缀树的节点
25+ type Node struct {
26+ // 如果一个node,end为空,不是结尾
27+ // 如果end不为空,表示这个点是某个字符串的结尾,end的值就是这个字符串
28+ End string
29+ // 只有在上面的end变量不为空的时候,endUse才有意义
30+ // 表示,这个字符串之前有没有加入过答案
31+ EndUse bool
32+ Fail *Node
33+ // 假设前缀树的节点上的值只是小写字母,有26个指向。经典前缀树
34+ Nexts []*Node
35+ }
36+
37+ func InitACAutomationNode () *Node {
38+ root := &Node{
39+ End: " " ,
40+ EndUse: false ,
41+ Fail: new (Node),
42+ Nexts: make ([]*Node, 26 ),
4643 }
47-
48- // AC自动机
49- public static class ACAutomation {
50- private Node root;
51-
52- // 建头结点
53- public ACAutomation () {
54- root = new Node ();
55- }
44+ return root
45+ }
5646
57- // 先建前缀树,建好之后再build所有节点的fail指针
58- public void insert (String s ) {
59- char [] str = s. toCharArray();
60- Node cur = root;
61- int index = 0 ;
62- for (int i = 0 ; i < str. length; i++ ) {
63- index = str[i] - ' a' ;
64- if (cur. nexts[index] == null ) {
65- Node next = new Node ();
66- cur. nexts[index] = next;
67- }
68- cur = cur. nexts[index];
69- }
70- cur. end = s;
47+ // insert 先建前缀树,建好之后再build所有节点的fail指针
48+ func (root *Node ) insert (s string ) {
49+ str := []byte (s)
50+ cur := root
51+ index := 0
52+ for i := 0 ; i < len (str); i++ {
53+ index = int (str[i] - ' a' )
54+ if cur.Nexts [index] == nil {
55+ next := InitACAutomationNode ()
56+ cur.Nexts [index] = next
7157 }
58+ cur = cur.Nexts [index]
59+ }
60+ cur.End = s
61+ }
7262
73- // 建立所有节点的fail指针
74- public void build () {
75- Queue< Node > queue = new LinkedList<> ();
76- queue. add( root);
77- Node cur = null ;
78- Node cfail = null ;
79- while ( ! queue . isEmpty()) {
80- // 当前节点弹出,
81- // 当前节点的所有后代加入到队列里去 ,
82- // 当前节点给它的子去设置fail指针
83- // cur -> 父亲
84- cur = queue . poll();
85- for ( int i = 0 ; i < 26 ; i ++ ) { // 所有的路
86- if (cur . nexts[i] != null ) { // 找到所有有效的路
87- cur . nexts[i] . fail = root; //
88- cfail = cur . fail;
89- while (cfail != null ) {
90- if (cfail . nexts [i] != null ) {
91- cur . nexts[i] . fail = cfail . nexts[i];
92- break ;
93- }
94- cfail = cfail. fail;
95- }
96- queue . add(cur . nexts[i]);
63+ // 建立所有节点的fail指针
64+ func ( root * Node ) build () {
65+ queue := make ([]*Node, 0 )
66+ queue = append (queue, root)
67+ var cur *Node
68+ var cfail *Node
69+
70+ for len (queue) != 0 {
71+ // 当前节点弹出 ,
72+ // 当前节点的所有后代加入到队列里去,
73+ // 当前节点给它的子去设置fail指针
74+ // cur -> 父亲
75+ cur = queue[ 0 ]
76+ queue = queue[ 1 :]
77+
78+ for i := 0 ; i < 26 ; i++ { // 所有的路
79+ if cur != nil && cur. Nexts != nil && cur. Nexts [i] != nil { // 找到所有有效的路
80+ cur. Nexts [i]. Fail = root
81+ cfail = cur. Fail
82+
83+ for cfail != nil {
84+ if cfail. Nexts != nil && cfail.Nexts [i] != nil {
85+ cur. Nexts [i]. Fail = cfail. Nexts [i]
86+ break
9787 }
88+ cfail = cfail.Fail
9889 }
90+ queue = append (queue, cur.Nexts [i])
9991 }
10092 }
93+ }
94+ }
10195
102- // build好之后,可以查文章有哪些候选串
103- public List<String > containWords (String content ) {
104- char [] str = content. toCharArray();
105- Node cur = root;
106- Node follow = null ;
107- int index = 0 ;
108- List<String > ans = new ArrayList<> ();
109- for (int i = 0 ; i < str. length; i++ ) {
110- index = str[i] - ' a' ; // 路
111- // 如果当前字符在这条路上没配出来,就随着fail方向走向下条路径
112- while (cur. nexts[index] == null && cur != root) {
113- cur = cur. fail;
114- }
115- // 1) 现在来到的路径,是可以继续匹配的
116- // 2) 现在来到的节点,就是前缀树的根节点
117- cur = cur. nexts[index] != null ? cur. nexts[index] : root;
118- follow = cur;
119- while (follow != root) {
120- if (follow. endUse) {
121- break ;
122- }
123- // 不同的需求,在这一段之间修改
124- if (follow. end != null ) {
125- ans. add(follow. end);
126- follow. endUse = true ;
127- }
128- // 不同的需求,在这一段之间修改
129- follow = follow. fail;
130- }
131- }
132- return ans;
96+
97+ // build好之后,可以查文章有哪些候选串
98+ func (root *Node ) containWords (content string ) []string {
99+ str := []byte (content)
100+
101+ cur := root
102+ var follow *Node
103+ ans := make ([]string , 0 )
104+
105+ for i := 0 ; i < len (str); i++ {
106+ index := int (str[i] - ' a' ) // 路
107+ // 如果当前字符在这条路上没配出来,就随着fail方向走向下条路径
108+ for cur.Nexts [index] == nil && cur != root {
109+ cur = cur.Fail
133110 }
134111
135- }
112+ // 1) 现在来到的路径,是可以继续匹配的
113+ // 2) 现在来到的节点,就是前缀树的根节点
114+ if cur.Nexts [index] != nil {
115+ cur = cur.Nexts [index]
116+ } else {
117+ cur = root
118+ }
119+ follow = cur
120+
121+ for follow != root {
122+ if follow.EndUse {
123+ break
124+ }
136125
137- public static void main (String [] args ) {
138- ACAutomation ac = new ACAutomation ();
139- ac. insert(" dhe" );
140- ac. insert(" he" );
141- ac. insert(" abcdheks" );
142- // 设置fail指针
143- ac. build();
144-
145- List<String > contains = ac. containWords(" abcdhekskdjfafhasldkflskdjhwqaeruv" );
146- for (String word : contains) {
147- System . out. println(word);
126+ // 不同的需求,在这一段之间修改
127+ if len (follow.End ) != 0 {
128+ ans = append (ans, follow.End )
129+ follow.EndUse = true
130+ }
131+ // 不同的需求,在这一段之间修改
132+ follow = follow.Fail
148133 }
149134 }
150-
135+ return ans
151136}
152137
138+ // he
139+ // abcdheks
140+ func main () {
141+ ac := InitACAutomationNode ()
142+ ac.insert (" ahe" )
143+ ac.insert (" he" )
144+ ac.insert (" abcdheks" )
145+ // 设置fail指针
146+ ac.build ()
147+
148+ contains := ac.containWords (" abcdhekskdjfafhasldkflskdjhwqaeruv" )
149+ for _ , word := range contains {
150+ fmt.Println (word)
151+ }
152+ }
153153```
154154
155155
0 commit comments