多pattern的匹配, 比KMP更general
public class ACAutomata {
static final int R = 256;
private class Node {
Node next[] = new Node[R];
Node fail;
String prefix;
int count;
}
public Node root = new Node();
ACAutomata(String[] patterns) {
for (String s : patterns) insert(s);
buildFailPointers();
}
private Node insert(Node root, String word, int i) {
if (root == null) root = new Node();
root.prefix = word.substring(0, i);
if (i == word.length()) {
root.count += 1;
return root;
}
root.next[word.charAt(i)] = insert(root.next[word.charAt(i)], word, i + 1);
return root;
}
private void insert(String word) {
root = insert(root, word, 0);
}
private void buildFailPointers() {
root.fail = null;
Queue<Node> q = new LinkedList<Node>();
for (Node x : root.next) {
if (x != null) {
x.fail = root;
q.offer(x);
}
}
for (; !q.isEmpty(); ) {
Node x = q.poll();
for (int i = 0; i < R; ++i) {
if (x.next[i] != null) {
Node p = x.fail;
for (; p != null && p.next[i] == null; p = p.fail) ;
x.next[i].fail = p != null ? p.next[i] : root;
q.offer(x.next[i]);
}
}
}
}
public int match(String text) {
Node x = root;
int num = 0;
for (int i = 0; i < text.length(); ++i) {
if (x.next[text.charAt(i)] != null)
x = x.next[text.charAt(i)]; // match case
else if (x != root)
x = x.fail.next[text.charAt(i)] != null ? x.fail.next[text.charAt(i)] : root;
if (x.count > 0 ) {
num += x.count;
System.out.println(x.prefix + " at " + (i - x.prefix.length() + 1));
}
}
return num;
}
}