@@ -10,146 +10,146 @@ AC自动机要解决的问题是,在一个文章中,有一些候选字符串
10
10
11
11
为每一个候选串建立一个前缀树,每个树节点都有一个fail指针。头节点fail指针人为规定指向null,第一层节点的fail指针人为规定,指向头节点。建立好前缀树后,宽度优先遍历设置全部的fail指针
12
12
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 ),
46
43
}
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
+ }
56
46
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
71
57
}
58
+ cur = cur.Nexts [index]
59
+ }
60
+ cur.End = s
61
+ }
72
62
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
97
87
}
88
+ cfail = cfail.Fail
98
89
}
90
+ queue = append (queue, cur.Nexts [i])
99
91
}
100
92
}
93
+ }
94
+ }
101
95
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
133
110
}
134
111
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
+ }
136
125
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
148
133
}
149
134
}
150
-
135
+ return ans
151
136
}
152
137
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
+ }
153
153
```
154
154
155
155
0 commit comments