HDU 2896 病毒侵袭 AC自动机

来源:互联网 发布:歌曲串烧制作软件 编辑:程序博客网 时间:2024/05/02 02:40

AC自动机模板题之一


可能我过几天就忘记了……



注意1:这题字符不是小写字母,是所有可见字符。


回忆:

 * AC自动机,令g[i,j]表示从i到j这一路遍历的所有字符串。 f[i]的意义就是g[?,i]和g[0,f[i]]的字符串是相等的
 * last[i] ,表示g[0,last[i]]的字符串,是确定存在的,并且以last[i]结尾的字符串

last的定义:

last[will] = val[f[will]] ? f[will] : last[f[will]];

这题是标准AC自动机模板


ac code:


#include<cstring>#include <cmath>#include <iostream>#include<queue>#include<cstdio>#include<map>#include<string>using namespace std;const int SIGMA_SIZE = 130;const int MAXNODE = 200 * 500 + 100;#define prln(x)cout<<#x<<" = "<<x<<endl#define pr(x)cout<<#x<<" = "<<x<<" "int n, m, X, Y;/* * AC自动机,令g[i,j]表示从i到j这一路遍历的所有字符串。 f[i]的意义就是g[?,i]和g[0,f[i]]的字符串是相等的 * last[i] ,表示g[0,last[i]]的字符串,是确定存在的,并且以last[i]结尾的字符串*/struct AhoCorasickAutomata {int ch[MAXNODE][SIGMA_SIZE];int f[MAXNODE];    // fail函数int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的valint last[MAXNODE]; // 输出链表的下一个结点int sz;queue<int>q;map<int,int>mp;int tot_web;void init() {sz = 1;memset(ch[0], 0, sizeof(ch[0]));memset(val, 0, sizeof(val));tot_web=0;}// 字符c的编号int idx(char c) {  //if (c == '\0') return 62;  /*if (c >= '0' && c <= '9') return c - '0';  if (c >= 'a' && c <= 'z') return c - 'a' + 10;  return c - 'A' + 36;  */return (int)c;}  // 插入字符串。v必须非0void insert(char s[], int len, int id) {int now = 0;for(int i = 0; i < len; i++) {int c = idx(s[i]);if(!ch[now][c]) {memset(ch[sz], 0, sizeof(ch[sz]));val[sz] = 0;ch[now][c] = sz++;}now = ch[now][c];}val[now] = id;//单词出现的次数}// 递归打印以结点j结尾的所有字符串void print(int j) //输出j节点的信息,如果last[j]存在,last[j]的位置也有字符{if(j) {mp[val[j]]=1;print(last[j]);}}// 在T中找模板,text串的下标从0开始,长度为lenvoid find(char text[], int len, int id) {mp.clear();int j = 0; // 当前结点编号,初始为根结点for(int i = 0; i < len; i++) { // 文本串当前指针int c = idx(text[i]);//while(j && !ch[j][c]) j = f[j]; // 顺着细边走,直到可以匹配j = ch[j][c];if(val[j]) print(j);else if(last[j]) print(last[j]); // 找到了!}if (mp.size()){++tot_web;printf("web %d:", id);for (auto x : mp){printf(" %d", x.first);}printf("\n");}}//计算fail指针void get_fail(){f[0] = 0;//fail[i]表示,当匹配到某个位置失败,下一个自动的位置for (int c = 0; c < SIGMA_SIZE; c++){int will = ch[0][c];if (will){f[will]=0;q.push(will);last[will] = 0;}}while (!q.empty()){int now = q.front();//prln(now);q.pop();for (int c = 0; c < SIGMA_SIZE; ++ c){int will = ch[now][c];//now节点,想要访问的下标//if (!will)continue;//如果这个下标不存在,直接continue;if (!will){ch[now][c] = ch[f[now]][c];continue;}q.push(will);int pre = f[now];//失配指针,先指now的失配,至少有一段都是相等的while (pre && !ch[pre][c])pre = f[pre];//往前跳失配指针,类似 KMPf[will] = ch[pre][c];// f[i]的意义就是g[?,i]和g[0,f[i]]的字符串是相等的last[will] = val[f[will]] ? f[will] : last[f[will]];}}}void doit(){printf("total: %d\n", tot_web);// * 调试ac自动机数组信息用的/*for (int i = 0; i < sz; ++ i){prln(i);for (int j = 0; j < 26;++j){if (ch[i][j])cout<< ((char)(j+'a'))<<" "<<ch[i][j]<<endl;;}//cout<<endl;//cout<<endl;}for (int i = 0; i < sz; ++ i){pr(i),prln(val[i]);}for (int i = 0; i < sz; ++ i){pr(i),prln(f[i]);}for (int i = 0; i < sz; ++ i){pr(i),prln(last[i]);}*/}}ac;char text[100000];char pattern[510];int main() {while (~scanf("%d", &n)){ac.init();for (int i = 1; i <= n; ++ i){scanf("%s", pattern);//cout<<pattern<<endl;ac.insert(pattern, strlen(pattern), i);}ac.get_fail();scanf("%d", &m);for (int i = 1; i <= m; ++ i){scanf("%s", text);//cout<<text<<endl;ac.find(text, strlen(text), i);}ac.doit();}return 0;}


0 0
原创粉丝点击