前言

作为一名菜鸡选手,我的目的仅仅是初步了解ac自动机的原理和各种性质。。。

正篇

1.AC自动机的基本性质

AC自动机的前置技能点 KMP,字典树
ac自动机的原理解释可以参考
hihocode hiho一下
我解释不清楚
1。跟后缀自动机不一样,ac自动机,是一张有向有环图,后缀自动机是有向无环图。
2。ac自动机是多对一的多模式串匹配。
3。ac自动机的常用的属性有 下面几个

1.val[i] 记录编号为i的节点的性质,一般用于记录,是否是一个模式串的终点
2.last[i] 记录编号为i的节点后缀链接,既 如果last[i] 不为 0 说明编号last[i]的节点为一个模式串,且是编号为i的字符串的最长后缀。(跟后缀自动机的后缀链接是一个东西)
3. fail[i] fail指针,跟kmp一样 表示编号为i的节点失配后,应该转移到哪一个状态。
4. ch[i][j] 状态转移方程 表示 编号为i的节点 下一个字符接上j的话,应该匹配到哪一个状态。

2.AC自动机的构建步骤

  1. 建立字典树。
    对于val数组 ,一般在建字典树的时候就要处理完成。 对于ch数组,在建字典树的时候会‘’初步‘’处理完成。*这时候ch表示的是一颗树

  2. 构建fail指针
    原理,我讲了也没(wo)人)(ye)听(bu)的(hui)懂。
    在构建玩fail指针后,会构建出上述的常用属性。并且会将字典树补成一张有向有环图。
    这个时候ch已经不再是字典树当中的那种定义了,ch[i][j]它表示编号为i的节点能匹配到的最优位置。

然后,就可以结合题意 ,运用ac自动机的属性来解决问题了。

3.实践

AC自动机的题目,其实就是对trie图的属性运用。
下面,列出一些题目,介绍一下ac自动机的常见用法。
1. hdu2222
ac自动机模板题,随便都能过,唯一可以注意的是,因为题目要求的是求字符串匹配,所以暴力往回跳的时候,可以不用fail指针往回跳,直接使用last既可,复杂度O(len * N) 但实际上不可能有这么多,

#include <bits/stdc++.h>#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 2e6 + 5;
const int maxn = 2e6 + 100;const int SIGMA_SIZE = 26;
const int MAXNODE = 100000 * 50;
const int MAXS = 150 + 10;
struct ACautomata {int ch[MAXNODE][SIGMA_SIZE];int f[MAXNODE];    // fail函数int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的valint last[MAXNODE]; // 输出链表的下一个结点int sz;int d[MAXNODE];void init() {sz = 1;memset (ch[0], 0, sizeof (ch[0]) );memset(d, 0, sizeof(d));}// 字符c的编号inline int idx (char c) {return c - 'a';}// 插入字符串。v必须非0void insert (char *s) {int u = 0, n = strlen (s);for (int i = 0; i < n; i++) {int c = idx (s[i]);//printf("%c", s[i]);if (!ch[u][c]) {memset (ch[sz], 0, sizeof (ch[sz]) );val[sz] = 0;ch[u][c] = sz++;}u = ch[u][c];//printf("%d", u);//puts("");}val[u] += 1;}// 递归打印匹配文本串str[i]结尾的后缀,以结点j结尾的所有字符串void print (int i, int j) {if (j) {print (i, last[j]);}}// 在T中找模板void find (char* T) {int n = strlen (T);int j = 0; // 当前结点编号,初始为根结点for (int i = 0; i < n; i++) { // 文本串当前指针int c = idx (T[i]);j = ch[j][c];if (val[j]) print (i, j);else if (last[j]) print (i, last[j]); // 找到了!}}// 计算fail函数void getFail() {queue<int> q;f[0] = 0;// 初始化队列for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[0][c];if (u) {f[u] = 0;q.push (u);last[u] = 0;}}// 按BFS顺序计算failwhile (!q.empty() ) {int r = q.front();q.pop();for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[r][c];if (!u) {ch[r][c] = ch[f[r]][c];continue;}q.push (u);int v = f[r];while (v && !ch[v][c]) v = f[v];f[u] = ch[v][c];last[u] = val[f[u]] ? f[u] : last[f[u]];}}}int query(char *s) {int len =strlen(s);int now = 0;int ans =0;for(int i=0; i<len; i++) {now = ch[now][s[i]-'a'];int tmp = now;while(now) {if(val[now]) {ans+=val[now];val[now]=0;}now = last[now];}now =tmp;}return ans;}
} ac;char s[MAXN];int main() {int n;int T;cin>>T;while(T--) {cin >> n;ac.init();for (int i = 1; i <= n; i++) {scanf ("%s", s);ac.insert (s);}ac.getFail();scanf ("%s", s );//ac.find(s);printf("%d\n",ac.query(s));}return 0;
}

2.
HDU - 2896
同样的模板题,可以注意的地方跟hdu2222 是一样的。

#include <bits/stdc++.h>#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 1e5;
const int maxn = 2e6 + 100;const int SIGMA_SIZE = 130;
const int MAXNODE = 1e5+10;
const int MAXS = 150 + 10;
struct ACautomata {int ch[MAXNODE][SIGMA_SIZE];int f[MAXNODE];    // fail函数int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的valint last[MAXNODE]; // 输出链表的下一个结点int sz;//int d[MAXNODE];void init() {sz = 1;memset (ch[0], 0, sizeof (ch[0]) );//memset(d, 0, sizeof(d));}// 字符c的编号inline int idx (char c) {return (int)c;}// 插入字符串。v必须非0void insert (char *s,int ids) {int u = 0, n = strlen (s);for (int i = 0; i < n; i++) {int c = idx (s[i]);//printf("%c", s[i]);if (!ch[u][c]) {memset (ch[sz], 0, sizeof (ch[sz]) );val[sz] = 0;ch[u][c] = sz++;}u = ch[u][c];}val[u] =ids;}// 递归打印匹配文本串str[i]结尾的后缀,以结点j结尾的所有字符串void print (int i, int j) {if (j) {print (i, last[j]);}}// 在T中找模板void find (char* T) {int n = strlen (T);int j = 0; // 当前结点编号,初始为根结点for (int i = 0; i < n; i++) { // 文本串当前指针int c = idx (T[i]);j = ch[j][c];if (val[j]) print (i, j);else if (last[j]) print (i, last[j]); // 找到了!}}// 计算fail函数void getFail() {queue<int> q;f[0] = 0;// 初始化队列for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[0][c];if (u) {f[u] = 0;q.push (u);last[u] = 0;}}// 按BFS顺序计算failwhile (!q.empty() ) {int r = q.front();q.pop();for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[r][c];if (!u) {ch[r][c] = ch[f[r]][c];continue;}q.push (u);int v = f[r];while (v && !ch[v][c]) v = f[v];f[u] = ch[v][c];last[u] = val[f[u]] ? f[u] : last[f[u]];}}}void query(char *s,vector<int> &V) {V.clear();int len =strlen(s);int now = 0;int ans =0;for(int i=0; i<len; i++) {now = ch[now][(int)s[i]];int tmp = now;while(now) {if(val[now]) {V.push_back(val[now]);}now = f[now];}now =tmp;}}
} ac;char s[MAXN];int main() {int n;while(cin>>n) {ac.init();for (int i = 1; i <= n; i++) {scanf ("%s", s);ac.insert (s,i);}ac.getFail();cin>>n;int tot=0;vector<int> V;for(int i=0;i<n;i++){scanf ("%s",s);ac.query(s,V);if(V.size()){sort(V.begin(),V.end());V.erase(unique(V.begin(),V.end()),V.end());tot++;printf("web %d:",i+1);for(int i=0;i<V.size();i++){printf(" %d",V[i]);}puts("");}}printf("total: %d\n",tot);}return 0;
}

3.
HDU-3065
模板题

#include <bits/stdc++.h>#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 1e5;
const int maxn = 2e6 + 100;const int SIGMA_SIZE = 130;
const int MAXNODE =101000;
const int MAXS = 1005;
char str[1005][100];
char s[2000005];
int ans[10000];
struct ACautomata {int ch[MAXNODE][SIGMA_SIZE];int f[MAXNODE];    // fail函数int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的valint last[MAXNODE]; // 输出链表的下一个结点int sz;//int d[MAXNODE];void init() {sz = 1;memset (ch[0], 0, sizeof (ch[0]) );memset(ans,0,sizeof ans);//memset(d, 0, sizeof(d));}// 字符c的编号inline int idx (char c) {return (int)c;}// 插入字符串。v必须非0void insert (char *s,int ids) {int u = 0, n = strlen (s);for (int i = 0; i < n; i++) {int c = idx (s[i]);//printf("%c", s[i]);if (!ch[u][c]) {memset (ch[sz], 0, sizeof (ch[sz]) );val[sz] = 0;ch[u][c] = sz++;}u = ch[u][c];}val[u] =ids;}// 递归打印匹配文本串str[i]结尾的后缀,以结点j结尾的所有字符串void print (int i, int j) {if (j) {print (i, last[j]);}}// 在T中找模板void find (char* T) {int n = strlen (T);int j = 0; // 当前结点编号,初始为根结点for (int i = 0; i < n; i++) { // 文本串当前指针int c = idx (T[i]);j = ch[j][c];if (val[j]) print (i, j);else if (last[j]) print (i, last[j]); // 找到了!}}// 计算fail函数void getFail() {queue<int> q;f[0] = 0;// 初始化队列for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[0][c];if (u) {f[u] = 0;q.push (u);last[u] = 0;}}// 按BFS顺序计算failwhile (!q.empty() ) {int r = q.front();q.pop();for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[r][c];if (!u) {ch[r][c] = ch[f[r]][c];continue;}q.push (u);int v = f[r];while (v && !ch[v][c]) v = f[v];f[u] = ch[v][c];last[u] = val[f[u]] ? f[u] : last[f[u]];}}}void query(char *s) {int len =strlen(s);int now = 0;for(int i=0; i<len; i++) {now = ch[now][idx(s[i])];int tmp = now;while(now) {if(val[now]) {ans[val[now]]++;}now = last[now];}now =tmp;}}
} ac;
int main() {int n;while(cin>>n) {ac.init();for (int i = 1; i <= n; i++) {scanf ("%s", str[i]);ac.insert (str[i],i);}ac.getFail();scanf("%s",s);ac.query(s);for(int i=1;i<=n;i++){if(!ans[i]) continue;printf("%s: %d\n",str[i],ans[i]);}}return 0;
}

4.
poj-2778
这题还是很有意思滴,利用里AC自动机的性质,因为ac自动机是一张图,任意两个点之间右边,就说明他们两个一定有一个是另一个的子串或者是后缀。 利用这个建立邻接矩阵,跑矩阵快速幂即可。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <iostream>
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
typedef pair<int, int> pii;
const int MOD = 100000;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 1e5;
const int maxn = 2e6 + 100;const int SIGMA_SIZE = 130;
const int MAXNODE =101000;
const int MAXS = 1005;
char str[1005][100];
char s[2000005];
int ans[10000];
int maps[300];
struct ACautomata {int ch[MAXNODE][SIGMA_SIZE];int f[MAXNODE];    // fail函数int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的valint last[MAXNODE]; // 输出链表的下一个结点int sz;//int d[MAXNODE];void init() {sz = 1;memset (ch[0], 0, sizeof (ch[0]) );//memset(d, 0, sizeof(d));}// 字符c的编号inline int idx (char c) {return maps[c];}// 插入字符串。v必须非0void insert (char *s,int ids) {int u = 0, n = strlen (s);for (int i = 0; i < n; i++) {int c = idx (s[i]);//printf("%c", s[i]);if (!ch[u][c]) {memset (ch[sz], 0, sizeof (ch[sz]) );val[sz] = 0;ch[u][c] = sz++;}u = ch[u][c];}val[u] =ids;}// 递归打印匹配文本串str[i]结尾的后缀,以结点j结尾的所有字符串void print (int i, int j) {if (j) {print (i, last[j]);}}// 在T中找模板void find (char* T) {int n = strlen (T);int j = 0; // 当前结点编号,初始为根结点for (int i = 0; i < n; i++) { // 文本串当前指针int c = idx (T[i]);j = ch[j][c];if (val[j]) print (i, j);else if (last[j]) print (i, last[j]); // 找到了!}}// 计算fail函数void getFail() {queue<int> q;f[0] = 0;// 初始化队列for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[0][c];if (u) {f[u] = 0;q.push (u);last[u] = 0;}}// 按BFS顺序计算failwhile (!q.empty() ) {int r = q.front();q.pop();for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[r][c];if (!u) {ch[r][c] = ch[f[r]][c];continue;}q.push (u);int v = f[r];while (v && !ch[v][c]) v = f[v];f[u] = ch[v][c];last[u] = val[f[u]] ? f[u] : last[f[u]];}}}void query(char *s) {int len =strlen(s);int now = 0;for(int i=0; i<len; i++) {now = ch[now][idx(s[i])];int tmp = now;while(now) {if(val[now]) {ans[val[now]]++;}now = last[now];}now =tmp;}}
} ac;
//<---------------------quickpow----------------------->
const int matX = 1e2 + 5;
const int mod = 100000;
struct Matrix {int n, m, s[matX][matX];Matrix(int n, int m): n(n), m(n) {for(int i = 0; i < n; i++) {for(int j = 0; j < n; j++) s[i][j] = 0;//s[i][i]=1;}}Matrix operator*(const Matrix &P)const {Matrix ret(n, P.m);for(int i = 0; i < n; i++) {for(int k = 0; k < m; k++) {if(s[i][k]) {for(int j = 0; j < P.m; j++) {ret.s[i][j] = ((LL)s[i][k] * P.s[k][j] + ret.s[i][j]) % mod;}}}}return ret;}Matrix operator^(const LL &P)const {LL num = P;Matrix ret(n, m), tmp = *this;for(int i = 0; i < n; i++) ret.s[i][i] = 1;while(num) {if(num & 1) ret = ret * tmp;tmp = tmp * tmp;num >>= 1;}return ret;}
};
int main() {maps['A']=0;maps['C']=1;maps['T']=2;maps['G']=3;long long n,m;while(cin>>n>>m) {ac.init();for (int i = 1; i <= n; i++) {scanf ("%s", str[i]);ac.insert (str[i],1);}ac.getFail();Matrix E(ac.sz,ac.sz);for(int i=0; i<=ac.sz; i++) {if(ac.val[i] || ac.last[i]) {continue;}for(int j=0; j<4; j++) {//if(!ac.ch[i][j]) continue;if(ac.val[ac.ch[i][j]] || ac.last[ac.ch[i][j]]) {continue;}E.s[i][ac.ch[i][j]]++;}}E=E^m;long long ans =0 ;for(int i=0; i<E.n; i++) {ans+=E.s[0][i];ans%=MOD;}cout<<ans<<endl;}return 0;
}

5.HDU-2825
这题我觉得也很有意思。
要求长度为n的串中,包含不少于k种给出模式串的方案数。
最多给出m个模式串,m<=10
观察到m很小,应该要很轻松的联想的状压。
然后吧Tire图建出来 , 跑一个简单DP即可。
dp[26][MAXNODE][1050];// dp[i][j][k] 表示 第i个数,到达第j个状态,包含k个子串的方案数

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <iostream>
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
typedef pair<int, int> pii;
const int MOD = 20090717;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int SIGMA_SIZE = 30;
const int MAXNODE =255;
const int MAXS = 1005;
char str[1005];
char s[2000005];
int ans[10000];
int maps[300];
struct ACautomata {int ch[MAXNODE][SIGMA_SIZE];int f[MAXNODE];    // fail函数int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的valint last[MAXNODE]; // 输出链表的下一个结点int sz;//int d[MAXNODE];void init() {sz = 1;memset (ch[0], 0, sizeof (ch[0]) );//memset(d, 0, sizeof(d));}// 字符c的编号inline int idx (char c) {return c-'a';}// 插入字符串。v必须非0void insert (char *s,int ids) {int u = 0, n = strlen (s);for (int i = 0; i < n; i++) {int c = idx (s[i]);//printf("%c", s[i]);if (!ch[u][c]) {memset (ch[sz], 0, sizeof (ch[sz]) );val[sz] = 0;ch[u][c] = sz++;}u = ch[u][c];}val[u] =ids;}// 递归打印匹配文本串str[i]结尾的后缀,以结点j结尾的所有字符串void print (int i, int j) {if (j) {print (i, last[j]);}}// 在T中找模板void find (char* T) {int n = strlen (T);int j = 0; // 当前结点编号,初始为根结点for (int i = 0; i < n; i++) { // 文本串当前指针int c = idx (T[i]);j = ch[j][c];if (val[j]) print (i, j);else if (last[j]) print (i, last[j]); // 找到了!}}// 计算fail函数void getFail() {queue<int> q;f[0] = 0;// 初始化队列for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[0][c];if (u) {f[u] = 0;q.push (u);last[u] = 0;}}// 按BFS顺序计算failwhile (!q.empty() ) {int r = q.front();q.pop();for (int c = 0; c < SIGMA_SIZE; c++) {int u = ch[r][c];if (!u) {ch[r][c] = ch[f[r]][c];continue;}q.push (u);int v = f[r];while (v && !ch[v][c]) v = f[v];f[u] = ch[v][c];last[u] = val[f[u]] ? f[u] : last[f[u]];}}}void query(char *s) {int len =strlen(s);int now = 0;for(int i=0; i<len; i++) {now = ch[now][idx(s[i])];int tmp = now;while(now) {if(val[now]) {ans[val[now]]++;}now = last[now];}now =tmp;}}
} ac;
int dp[26][MAXNODE][1050];// dp[i][j][k] 表示 第i个数,到达第j个状态,包含k个子串的方案数
int num[2050];
int getsize(int a){int num=0;while(a){num+=a&1;a>>=1;}return num;
}
int main() {long long n,m,k;for(int i=0;i<2050;i++){num[i]=getsize(i);}while(cin>>n>>m>>k) {if(n==0 && m==0 && k==0) break;ac.init();for (int i = 1; i <= m; i++) {scanf ("%s", str);int lensssss = strlen(str);if(lensssss >n) continue;ac.insert (str,i);}ac.getFail();memset(dp,0,sizeof dp);int now=0;dp[0][0][0]=1;int v,tmp,nv;for(int i=0;i<n;i++){for(int j=0;j<ac.sz;j++){for(int ks = 0;ks<(1<<m);ks++){if(!dp[i][j][ks]) continue;for(int z=0;z<=25;z++){v= ac.ch[j][z];tmp = v;nv=ks;while(v){if(ac.val[v])nv|=(1<<(ac.val[v]-1));//cout<<i<<":"<<(char)(z+'a')<<","<<nv<<endl;v=ac.last[v];}
//                        if(nv==3){//                            printf("dp[%d][%d][%d]=%lld\n",i,j,ks,dp[i][j][ks]);
//                        }dp[i+1][tmp][nv]+=dp[i][j][ks];dp[i+1][tmp][nv]%=MOD;}}}}int ans =0;for(int j=0;j<ac.sz;j++){for(int ks=0;ks<(1<<m);ks++){if(num[ks]>=k)ans+=dp[n][j][ks];ans %= MOD;}}printf("%d\n",ans);}return 0;
}

未完待续。。。。。。。。

AC自动机 从入门到模板相关推荐

  1. AC自动机从入门到last优化

    AC自动机是KMP算法和Trie(字典树)的巧妙结合这篇文章主要讲针对几个例题给出解答模版(主要是知识点自己讲不清楚). 至于针对的知识点,给上几个我认为说的比较好的传送门,读者可以自行选择阅读.(我 ...

  2. HDU 6208 The Dominator of Strings AC自动机

    题目链接:HDU 6208 The Dominator of Strings Time Limit: 3000/3000 MS (Java/Others)    Memory Limit: 65535 ...

  3. AC自动机 HDOJ 5384 Danganronpa

    题目传送门 1 /* 2 题意:多个文本串,多个模式串在每个文本串出现的次数 3 AC自动机:这就是一道模板题,杭电有道类似的题目 4 */ 5 /************************** ...

  4. HDU 2222 ACAM模板(AC自动机)

    这里找到了两篇很nice的Trie树(作者Hackbuteer1)以及AC自动机(作者niushuai666)入门详解.博主写的可以说是非常用心了,一看就懂. 题意:给出N(<=10000)个单 ...

  5. AC自动机算法及模板

    AC自动机算法及模板 2016-05-08 18:58 226人阅读 评论(0) 收藏 举报  分类: AC自动机(1)  版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 关于 ...

  6. P5357 【模板】AC自动机(二次加强版)(AC自动机建fail树dfs求模式串出现次数)

    P5357 [模板]AC自动机(二次加强版)(AC自动机建fail树dfs求模式串出现次数) 传送门 形式上,AC 自动机基于由若干模式串构成的 Trie 树,并在此之上增加了一些 fail 边:本质 ...

  7. 模板 - AC自动机

    ACM-ICPC模板 目录 求有多少个模式串在文本串里出现过 建fail树dfs求每个模式串在文本串中的出现次数 ac自动机fail树上dfs序建可持久化线段树 AC自动机是一种多模匹配算法 AC自动 ...

  8. luogu P3808 【模板】AC自动机(简单版)

    二次联通门 : luogu P3808 [模板]AC自动机(简单版) /*luogu P3808 [模板]AC自动机(简单版)手速越来越快了10分钟一个AC自动机一遍过编译 + 一边AC感觉不错我也就 ...

  9. HDU2222 Keywords Search(AC自动机模板)

    AC自动机是一种多模式匹配的算法.大概过程如下: 首先所有模式串构造一棵Trie树,Trie树上的每个非根结点都代表一个从根出发到该点路径的字符串. 然后每个结点都计算出其fail指针的值,这个fai ...

最新文章

  1. alt+shift+j,添加日期、作者等
  2. [其实有加强版的]校门外的树
  3. Algorithm:C++/python语言实现之求旋转数组最小值、求零子数组、求最长公共子序列和最长公共子串、求LCS与字符串编辑距离
  4. [译]NeHe教程 - 你的第一个多边形
  5. hadoop 源码编译---Win7(64位)
  6. EOJ_1081_朋友圈
  7. express中路由配置优化
  8. enter opportunity line item detail page AG3
  9. Java 修饰符详解:吐血整理,带你从底层语法和开发案例疏通这 11 种常见修饰符
  10. linux重装lnmp,基于CentOS 6.5的LNMP安装过程
  11. 01-AD17下载安装教程(附下载链接)
  12. 软件工程实践——软件评测
  13. 电脑录屏软件哪个最好用?都是高效高清!
  14. 使用频谱仪测试信号抗干扰实验
  15. easycode小帮手
  16. 迷宫 做的第一个搜索题
  17. z-stac配置按键
  18. 12306订票流程解析
  19. #分析方法#波士顿矩阵
  20. 华文慕课北大操作系统陈向群第五章课后习题解析

热门文章

  1. C++入门到精通(xcode IDE)
  2. 第十八章 Caché 命令大全 NEW 命令
  3. excel下拉菜单vba_Excel 2007的经典菜单
  4. 【题】【贪心】NKOJ3827 火车运输
  5. AMAZINGPANDAVERSE打造2.0元宇宙生态的能量杠杆
  6. 测试/开发程序员的思考,突破变得更强......
  7. 门电路图对照表——国标、IEEE、标准符号对照表
  8. caffe深度学习框架入门
  9. 游标v_cur的%notfound
  10. 怎么把文字转换成语音?教你几个方法,超级简单