AC自动机
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
=== Darksun_140305 ===
{{{
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int MaxC=62; /* 最大字符种数 */
struct Trie_Node{
int next[MaxC],last,fail,val;
} ept; /* last为后缀链接,fail为失配边,val为节点值 */
/* val==0表示该点不是模板串的结尾 */
/* val!=0表示该点是模板串的结尾,且该模板串有val个后缀是模板串 */
typedef vector <Trie_Node> AC;
AC A;
int idx(char c){
if ('0'<=c && c<='9') return c-'0';
if ('a'<=c && c<='z') return c-'a'+10;
if ('A'<=c && c<='Z') return c-'A'+36;
}
void insert(char *s, AC &A){
int len=strlen(s);
int i,j,u=0;
for (i=0; i<len; i++){
int v=idx(s[i]);
if (!A[u].next[v]){
A.push_back(ept);
A[u].next[v]=A.size()-1;
}
u=A[u].next[v];
}
A[u].val=1;
} /* 建立模板的Trie */
void getFail(AC &A){
queue <int> Q;
for (int c=0; c<MaxC; c++){
int u=A[0].next[c];
if (u) Q.push(u);
}
while (!Q.empty()){
int u=Q.front(); Q.pop();
for (int c=0; c<MaxC; c++){
int v=A[u].next[c];
if (!v){ A[u].next[c]=A[A[u].fail].next[c]; continue; }
/* 如果u点没有c边,则直接变成失配边 */
Q.push(v);
int t=A[u].fail;
while (t && !A[t].next[c]) t=A[t].fail;
/* 沿着失配边走,直到找到等于当前串后缀的串,或者回0点 */
int tmp=A[t].next[c];
A[v].fail=tmp;
A[v].last=A[tmp].val? tmp: A[tmp].last;
if (A[v].val) A[v].val+=A[A[v].last].val;
}
}
} /* BFS建立自动机的失配边 */
const int MN=1e6+7;
int t[MN];
void calc(int u){
if (u){ t[u]++; calc(A[u].last); }
}
void match(char *s){
int len=strlen(s);
int i,j=0;
for (i=0; i<len; i++){
int c=idx(s[i]);
j=A[j].next[c];
if (A[j].val) calc(j);
else if (A[j].last) calc(A[j].last);
}
} /* 匹配文本串,计算每个模板的出现次数 */
int main(){
int n;
while (scanf("%d",&n)!=EOF){
if (n==0) break;
A.clear(); A.push_back(ept);
memset(t,0,sizeof(t));
char ss[MN];
int i,j,k;
for (i=1; i<=n; i++){
scanf("%s",ss);
insert(ss,A);
} /* 读入模板 */
scanf("%s",ss); /* 读入文本 */
getFail(A);
match(ss);
int maxt=0;
for (i=1; i<A.size(); i++) maxt=max(maxt,t[i]);
printf("%d\n",maxt); /* 输出出现次数最高的模板的出现次数 */
}
return 0;
}
}}}
=== AIdancer ===
{{{
struct node {
int ct;
node *pre, *next[26];
void init() {
ct = 0;
pre = 0;
memset(next, 0, sizeof(next));
}
};
int cnt; //节点总数
node *root, trie[500010];
node *queue[500010];
void insertStr(node *root, char *str) {
int index, len = strlen(str);
for(int i = 0; i < len; i++) {
index = str[i]-'a';
if(root->next[index] == 0) {
root->next[index] = &trie[++cnt];
root->next[index]->init();
}
root = root->next[index];
}
root->ct++;
}
void buildAC() {
int head, tail;
node *u, tmp;
queue[0] = root;
root->pre = root;
head = tail = 0;
while(head <= tail) {
u = queue[head++];
for(int i = 0; i < 26; i++) {
if(u->next[i] == 0) {
if(u == root) u->next[i] = root;
else u->next[i] = u->pre->next[i];
}
else {
if(u == root) u->next[i]->pre = root;
else u->next[i]->pre = u->pre->next[i];
queue[++tail] = u->next[i];
}
}
}
}
}}}
Darksun_140305
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int MaxC=62; /* 最大字符种数 */
struct Trie_Node{
int next[MaxC],last,fail,val;
} ept; /* last为后缀链接,fail为失配边,val为节点值 */
/* val==0表示该点不是模板串的结尾 */
/* val!=0表示该点是模板串的结尾,且该模板串有val个后缀是模板串 */
typedef vector <Trie_Node> AC;
AC A;
int idx(char c){
if ('0'<=c && c<='9') return c-'0';
if ('a'<=c && c<='z') return c-'a'+10;
if ('A'<=c && c<='Z') return c-'A'+36;
}
void insert(char *s, AC &A){
int len=strlen(s);
int i,j,u=0;
for (i=0; i<len; i++){
int v=idx(s[i]);
if (!A[u].next[v]){
A.push_back(ept);
A[u].next[v]=A.size()-1;
}
u=A[u].next[v];
}
A[u].val=1;
} /* 建立模板的Trie */
void getFail(AC &A){
queue <int> Q;
for (int c=0; c<MaxC; c++){
int u=A[0].next[c];
if (u) Q.push(u);
}
while (!Q.empty()){
int u=Q.front(); Q.pop();
for (int c=0; c<MaxC; c++){
int v=A[u].next[c];
if (!v){ A[u].next[c]=A[A[u].fail].next[c]; continue; }
/* 如果u点没有c边,则直接变成失配边 */
Q.push(v);
int t=A[u].fail;
while (t && !A[t].next[c]) t=A[t].fail;
/* 沿着失配边走,直到找到等于当前串后缀的串,或者回0点 */
int tmp=A[t].next[c];
A[v].fail=tmp;
A[v].last=A[tmp].val? tmp: A[tmp].last;
if (A[v].val) A[v].val+=A[A[v].last].val;
}
}
} /* BFS建立自动机的失配边 */
const int MN=1e6+7;
int t[MN];
void calc(int u){
if (u){ t[u]++; calc(A[u].last); }
}
void match(char *s){
int len=strlen(s);
int i,j=0;
for (i=0; i<len; i++){
int c=idx(s[i]);
j=A[j].next[c];
if (A[j].val) calc(j);
else if (A[j].last) calc(A[j].last);
}
} /* 匹配文本串,计算每个模板的出现次数 */
int main(){
int n;
while (scanf("%d",&n)!=EOF){
if (n==0) break;
A.clear(); A.push_back(ept);
memset(t,0,sizeof(t));
char ss[MN];
int i,j,k;
for (i=1; i<=n; i++){
scanf("%s",ss);
insert(ss,A);
} /* 读入模板 */
scanf("%s",ss); /* 读入文本 */
getFail(A);
match(ss);
int maxt=0;
for (i=1; i<A.size(); i++) maxt=max(maxt,t[i]);
printf("%d\n",maxt); /* 输出出现次数最高的模板的出现次数 */
}
return 0;
}
AIdancer
struct node {
int ct;
node *pre, *next[26];
void init() {
ct = 0;
pre = 0;
memset(next, 0, sizeof(next));
}
};
int cnt; //节点总数
node *root, trie[500010];
node *queue[500010];
void insertStr(node *root, char *str) {
int index, len = strlen(str);
for(int i = 0; i < len; i++) {
index = str[i]-'a';
if(root->next[index] == 0) {
root->next[index] = &trie[++cnt];
root->next[index]->init();
}
root = root->next[index];
}
root->ct++;
}
void buildAC() {
int head, tail;
node *u, tmp;
queue[0] = root;
root->pre = root;
head = tail = 0;
while(head <= tail) {
u = queue[head++];
for(int i = 0; i < 26; i++) {
if(u->next[i] == 0) {
if(u == root) u->next[i] = root;
else u->next[i] = u->pre->next[i];
}
else {
if(u == root) u->next[i]->pre = root;
else u->next[i]->pre = u->pre->next[i];
queue[++tail] = u->next[i];
}
}
}
}