ac自动机:
给你若干个单词,判断一段文字里边这些单词出现了吗,在哪出现,出现几次?
首先是字典树模板:
#include<bits/stdc++.h> #define debug printf("!"); using namespace std; const int maxn=1e3+50; struct trie_node{ int count; trie_node*son[26]; }; trie_node* new_node() { trie_node* pnode=new trie_node(); pnode->count=0; for(int i=0;i<26;i++)pnode->son[i]=NULL; return pnode; } void trie_insert(trie_node *root,char key[]) { trie_node* node=root; char *p=key; while(*p) { if(node->son[*p-'a']==NULL)node->son[*p-'a']=new_node(); node=node->son[*p-'a']; ++p; } node->count+=1; } int trie_search(trie_node *root,char key[]) { trie_node*node=root; char *p=key; while(*p&&node!=NULL) { node=node->son[*p-'a']; ++p; } if(node==NULL)return 0; else return node->count; } int main() { int n,q,i,j; char s[20]; trie_node *root; root=new_node(); scanf("%d%d",&n,&q); while(n--) { scanf("%s",s); trie_insert(root,s); } while(q--) { scanf("%s",s); printf("%d ",trie_search(root,s)); } }
时隔几个月,觉得上面那个模板好丑,搬了oiwiki的板子:
struct trie { int nex[100000][26], cnt; bool exist[100000]; // 该结点结尾的字符串是否存在 void insert(char *s, int l) { // 插入字符串 int p = 0; for (int i = 0; i < l; i++) { int c = s[i] - 'a'; if (!nex[p][c]) nex[p][c] = ++cnt; // 如果没有,就添加结点 p = nex[p][c]; } exist[p] = 1; } bool find(char *s, int l) { // 查找字符串 int p = 0; for (int i = 0; i < l; i++) { int c = s[i] - 'a'; if (!nex[p][c]) return 0; p = nex[p][c]; } return exist[p]; } };
然后是ac自动机:字典树+fail指针 重点在getfail函数
例题是 hdu5880
一整天,一直被卡MLE,改成指针形式的动态开点也卡。
然后去搜,终于搜到一篇博客。
博客说:不要一次性memset,而是,用到一个点,memset一次。
#include<cstdio> #include<iostream> #include<cstring> #include<queue> #define debug printf("!"); using namespace std; const int maxn=1e6+5; struct T{ int fail; int vis[26]; int dep; }ac[maxn]; int cnt; char s[maxn]; void init(int t) { ac[t].fail=ac[t].dep=0; memset(ac[t].vis,0,sizeof(ac[t].vis)); } inline void insert(char *s) { int len=strlen(s),i,p=0,dep=0; for(i=0;i<len;i++) { dep++; if(!ac[p].vis[s[i]-'a']) { ac[p].vis[s[i]-'a']=++cnt; init(cnt); } p=ac[p].vis[s[i]-'a']; } ac[p].dep=dep; } inline void get_fail() { queue<int>que; for(int i=0;i<26;i++) { if(ac[0].vis[i]) { ac[ac[0].vis[i]].fail=0; que.push(ac[0].vis[i]); } } while(!que.empty()) { int u=que.front(); que.pop(); for(int i=0;i<26;i++) { if(ac[u].vis[i]!=0) { ac[ac[u].vis[i]].fail=ac[ac[u].fail].vis[i]; que.push(ac[u].vis[i]); } else ac[u].vis[i]=ac[ac[u].fail].vis[i]; } } } void query(char s[]) { int len=strlen(s),i,j,k,t=0,p=0; for(i=0;i<len;i++) { char c=s[i]; if('A'<=c&&c<='Z')c+=32; else if(!('a'<=c&&c<='z')) { p=0;continue; } p=ac[p].vis[c-'a']; for(j=p;j;j=ac[j].fail) { if(ac[j].dep) { for(k=i-ac[j].dep+1;k<=i;k++)s[k]='*'; } } } } int main() { int n,T; scanf("%d",&T); while(T--) { cnt=0; init(0); scanf("%d",&n); while(n--) { scanf("%s",s); insert(s); } get_fail(); getchar(); cin.getline(s,maxn); query(s); printf("%s ",s); } }
(上面的板子也不好看●﹏● 用下面的:
例题: 破忒头的匿名信
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=5e5+5; ll f[maxn]; int val[maxn],dep[maxn]; struct AC{ int tr[maxn][26],cnt; int fail[maxn]; void insert(char s[],int len,int w) { int p=0; for(int i=0;i<len;i++) { if(!tr[p][s[i]-'a'])tr[p][s[i]-'a']=++cnt; p=tr[p][s[i]-'a']; } dep[p]=len; if(!val[p])val[p]=w; val[p]=min(val[p],w); } void getfail() { queue<int>que; for(int i=0;i<26;i++)if(tr[0][i])que.push(tr[0][i]); while(!que.empty()) { int p=que.front();que.pop(); for(int i=0;i<26;i++) { if(tr[p][i])fail[tr[p][i]]=tr[fail[p]][i],que.push(tr[p][i]); else tr[p][i]=tr[fail[p]][i]; } } } void query(char s[],int len) { int j,p=0; for(int i=1;i<=len;i++) { j=p=tr[p][s[i]-'a']; while(j) { if(dep[j])f[i]=min(f[i],f[i-dep[j]]+val[j]); j=fail[j]; } } } }ac; char s[maxn]; int main() { int n,w,len; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s%d",s,&w); ac.insert(s,strlen(s),w); } ac.getfail(); scanf("%s",s+1); len=strlen(s+1); for(int i=1;i<=len;i++)f[i]=1e16; ac.query(s,len=strlen(s+1)); printf("%lld ",f[len]==1e16?-1:f[len]); }