Skip to content

Commit 928104a

Browse files
committed
ac
1 parent 57385c1 commit 928104a

File tree

1 file changed

+124
-124
lines changed

1 file changed

+124
-124
lines changed

24-《进阶》AC自动机和卡特兰数.md

Lines changed: 124 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)