标签:ace 前缀 target 自动 没有 入队 16px ret include
AC自动机……(至于自动机是啥我也看不懂……请自行百度)
AC自动机简单来说可以被看成是trie树和KMP算法的结合体,它的用途主要是多模匹配,就是给你一个文本串和多个模式串,询问你诸如:有多少个模式串在文本串中出现过,或是什么模式串在文本串中出现了多少次之类的。
AC自动机的重点在于fail数组的构造。fail数组简单来说可以指当前字符串的一个后缀在这个trie树中所对应的最长的,且能与该后缀匹配的前缀的末尾位置。是不是很像KMP?我们说一下怎么构造fail数组。
首先按照构造trie树的方法构造一下模式串的trie树。这个应该不用说了……
构造fail数组的方法是bfs。首先,对于根结点上所连的点,把他们的fail全都设为root(就是0),并且把他们压入队列。之后每次取队列首元素,对于其子节点(v),首先跳转到当前节点的fail数组的位置(k),找这个节点(k)有没有与v相同的子节点(u),如果有,那么v的fail就是u,否则的话就再找k的fail,一直到找到或者回到root为止。
看样子写起来会很长……不过有这样一份代码。
void getfail() { rep(i,0,25) if(c[0][i]) fail[c[0][i]] = 0,q.push(c[0][i]); while(!q.empty()) { int k = q.front();q.pop(); rep(i,0,25) { if(c[k][i]) fail[c[k][i]] = c[fail[k]][i],q.push(c[k][i]); else c[k][i] = c[fail[k]][i]; } } }
这个是不是非常短呀?
重点在于这一句:c[k][i] = c[fail[k]][i];像是并查集一样,这样我们在求fail的时候只要访问一次fail就可以知道它的fail应该是什么了。
因为家里电脑很爆炸……图先不画了……明天补上。
这样就非常容易的把fail数组建好了。
之后就是匹配了。匹配其实很简单,只要在每次匹配的时候都向其fail跳一次看看能否匹配即可。注意要先符合条件。在每次匹配上的时候,我们把匹配数目++。
这样简单版的就做好了……图明天补上。因为有上面类似并查集的操作,所以整个trie树变成了一个trie图,使得它即使匹配到末尾仍然能匹配回来。
增强版其实也并不难。我们只要在每次匹配的时候把匹配次数++,并且记录是哪个字符串匹配了即可。注意这里不能像简单版一样把一个节点的权值赋成-1,因为将来你还有可能再用,比如说在出现相同的字符串的时候。(这个在luogu上卡掉了我第一个点……图明天补上)
这样就可以了,看一下代码。
简单版:
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(‘\n‘) using namespace std; const int N = 500005; const int M = 1000005; typedef long long ll; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) op = -1; ch = getchar(); } while(ch >= ‘0‘ && ch <= ‘9‘) { ans *= 10; ans += ch - ‘0‘; ch = getchar(); } return ans * op; } queue<int> q; struct ACG { int c[N][26],val[N],fail[N],cnt; void ins(char *s) { int len = strlen(s); int now = 0; rep(i,0,len-1) { int v = s[i] - ‘a‘; if(!c[now][v]) c[now][v] = ++cnt; now = c[now][v]; } val[now]++; } void getfail() { rep(i,0,25) if(c[0][i]) fail[c[0][i]] = 0,q.push(c[0][i]);//对根的儿子求fail while(!q.empty())//bfs求fail { int k = q.front(); q.pop(); rep(i,0,25) { if(c[k][i]) fail[c[k][i]] = c[fail[k]][i],q.push(c[k][i]); else c[k][i] = c[fail[k]][i]; } } } int query(char *s) { int len = strlen(s); int now = 0,ans = 0; rep(i,0,len-1) { now = c[now][s[i]-‘a‘]; for(int t = now; t && val[t] != -1; t = fail[t]) ans += val[t],val[t] = -1; } return ans; } }AC; int n; char p[M]; int main() { n = read(); rep(i,1,n) scanf("%s",p),AC.ins(p); AC.getfail(); scanf("%s",p); int ans = AC.query(p); printf("%d\n",ans); return 0; }
增强版:
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(‘\n‘) using namespace std; const int N = 500005; const int M = 1000005; typedef long long ll; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) op = -1; ch = getchar(); } while(ch >= ‘0‘ && ch <= ‘9‘) { ans *= 10; ans += ch - ‘0‘; ch = getchar(); } return ans * op; } queue<int> q; int t,sum[200],maxn; char str[M],s[200][200]; struct ACG { int c[N][26],p[N],fail[N],cnt,d[N]; void clear() { memset(c,0,sizeof(c)); memset(fail,0,sizeof(fail)); memset(d,0,sizeof(d)); memset(p,0,sizeof(p)); cnt = 0; } void insert(char *s,int f) { int len = strlen(s),now = 0; rep(i,0,len-1) { int v = s[i] - ‘a‘; if(!c[now][v]) c[now][v] = ++cnt; now = c[now][v]; } p[now]++,d[now] = f; } void getfail() { rep(i,0,25) if(c[0][i]) fail[c[0][i]] = 0,q.push(c[0][i]); while(!q.empty()) { int k = q.front();q.pop(); rep(i,0,25) { if(c[k][i]) fail[c[k][i]] = c[fail[k]][i],q.push(c[k][i]); else c[k][i] = c[fail[k]][i]; } } } void match(char *s) { int len = strlen(s),now = 0; rep(i,0,len-1) { now = c[now][s[i] - ‘a‘]; for(int g = now;g && p[g] != -1;g = fail[g]) { if(p[g]) sum[d[g]]++; //else p[g] = -1; } } } }AC; int main() { while(1) { t = read(); if(!t) break; AC.clear(),memset(sum,0,sizeof(sum)),maxn = 0; rep(i,1,t) scanf("%s",s[i]),AC.insert(s[i],i); AC.getfail(); scanf("%s",str),AC.match(str); rep(i,1,t) maxn = max(maxn,sum[i]); printf("%d\n",maxn); rep(i,1,t) if(sum[i] == maxn) printf("%s\n",s[i]); } return 0; }
标签:ace 前缀 target 自动 没有 入队 16px ret include
原文地址:https://www.cnblogs.com/captain1/p/9557709.html