AC自动机

1、uva 1449/LA 4670 Dominating Patterns

  题意:有n个模板串和一个文本串,找出哪些模板串在文本串中出现次数最多。

  思路:AC自动机模板

  1 #include<cstring>
  2 #include<queue>
  3 #include<cstdio>
  4 #include<map>
  5 #include<string>
  6 using namespace std;
  7 
  8 const int SIGMA_SIZE = 26;
  9 const int MAXNODE = 11000;
 10 const int MAXS = 150 + 10;
 11 
 12 map<string, int> ms;
 13 
 14 struct ACTree
 15 {
 16     int ch[MAXNODE][SIGMA_SIZE];
 17     int f[MAXNODE];    // fail函数
 18     int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的val
 19     int last[MAXNODE]; // last[j]表示结点j沿着失配指针往回走时,遇到的下一个单词结点编号(后缀链接)
 20     int cnt[MAXS];
 21     int sz;
 22 
 23     void init()
 24     {
 25         sz = 1;
 26         memset(ch[0], 0, sizeof(ch[0]));
 27         memset(cnt, 0, sizeof(cnt));
 28         ms.clear();
 29     }
 30 
 31     // 字符c的编号
 32     int idx(char c)
 33     {
 34         return c - 'a';
 35     }
 36 
 37     // 插入字符串。v必须非0
 38     void insert(char *s, int v)
 39     {
 40         int u = 0, n = strlen(s);
 41         for (int i = 0; i < n; i++)
 42         {
 43             int c = idx(s[i]);
 44             if (!ch[u][c])
 45             {
 46                 memset(ch[sz], 0, sizeof(ch[sz]));
 47                 val[sz] = 0;
 48                 ch[u][c] = sz++;
 49             }
 50             u = ch[u][c];
 51         }
 52         val[u] = v;
 53         ms[string(s)] = v;
 54     }
 55 
 56     //统计以结点j结尾的所有字符串
 57     void Cnt(int j)
 58     {
 59         if (j)
 60         {
 61             cnt[val[j]]++;
 62             Cnt(last[j]);
 63         }
 64     }
 65 
 66     // 在T中找模板
 67     int find(char* T)
 68     {
 69         int n = strlen(T);
 70         int j = 0; // 当前结点编号,初始为根结点
 71         for (int i = 0; i < n; i++)
 72         { // 文本串当前指针
 73             int c = idx(T[i]);
 74             //while (j && !ch[j][c]) j = f[j]; // 顺着失配边走,直到可以匹配
 75             j = ch[j][c];
 76             if (val[j]) Cnt(j);
 77             else if (last[j]) Cnt(last[j]); // 找到了
 78         }
 79     }
 80 
 81     // 计算fail函数
 82     void getFail()
 83     {
 84         queue<int> q;
 85         f[0] = 0;
 86         // 初始化队列
 87         for (int c = 0; c < SIGMA_SIZE; c++)
 88         {
 89             int u = ch[0][c];
 90             if (u)
 91             {
 92                 f[u] = 0; q.push(u); last[u] = 0;
 93             }
 94         }
 95         // 按BFS顺序计算fail
 96         while (!q.empty())
 97         {
 98             int r = q.front(); q.pop();
 99             for (int c = 0; c < SIGMA_SIZE; c++)
100             {
101                 int u = ch[r][c];
102                 //if (!u) continue;
103                 if (!u)
104                 {
105                     ch[r][c] = ch[f[r]][c];
106                     continue;
107                 }
108                 q.push(u);
109                 int v = f[r];
110                 while (v && !ch[v][c]) v = f[v];
111                 f[u] = ch[v][c];
112                 last[u] = val[f[u]] ? f[u] : last[f[u]];
113             }
114         }
115     }
116 
117 };
118 
119 ACTree ac;
120 char text[1000001], P[151][80];
121 int n, T;
122 
123 int main()
124 {
125     while (scanf("%d", &n) == 1 && n)
126     {
127         ac.init();
128         for (int i = 1; i <= n; i++)
129         {
130             scanf("%s", P[i]);
131             ac.insert(P[i], i);
132         }
133         ac.getFail();
134         scanf("%s",text);
135         ac.find(text);
136         int best = -1;
137         for (int i = 1; i <= n; i++)
138             if (ac.cnt[i] > best) best = ac.cnt[i];
139         printf("%d\n", best);
140         for (int i = 1; i <= n; i++)
141             if (ac.cnt[ms[string(P[i])]] == best) printf("%s\n", P[i]);
142         //不用ac.cnt[i]则是考虑模板串重复的情况,重复时后一个串会覆盖前一个串
143     }
144     return 0;
145 }
View Code

 2、uva 11468 Substring

  题意:给定一些模式串,然后给出一些字母出现的概率,每次随机出现一个字母,要求出这些字母出现L个组成的字符串不包含(即不是它的连续字串)已有模式串的概率

  思路:先构造出AC自动机,构造的时候利用fail数组的特性,记录下每个位置是否有经过一个单词结点,如果有这个结点就是不能走的结点,那么问题就变成了只能在能走的结点上走L步的概率,然后进行dp即可。

  1 #include<cstdio>  
  2 #include<cstring>  
  3 #include<queue>
  4 #include<iostream>
  5 using namespace std;
  6 
  7 const int MAXNODE = 405;// 结点总数
  8 const int SIGMA_SIZE = 62;//大小写字母、数字
  9 
 10 struct ACTree
 11 {
 12 
 13     int ch[MAXNODE][SIGMA_SIZE];
 14     int val[MAXNODE];//val[i]表示结点i为字符串的结尾或后缀为单词
 15     int Fail[MAXNODE];
 16     int sz;
 17 
 18     double dp[MAXNODE][105];
 19     double p[SIGMA_SIZE];//存概率
 20     bool vis[MAXNODE][105];
 21 
 22     void init()
 23     {
 24         sz = 1;
 25         memset(ch[0], 0, sizeof(ch[0]));
 26         val[0] = 0;
 27 
 28         memset(p, 0, sizeof(p));
 29         memset(vis, false, sizeof(vis));
 30     }
 31 
 32     int idx(char c)
 33     {
 34         if (c >= 'a' && c <= 'z') return c - 'a';
 35         if (c >= 'A' && c <= 'Z') return c - 'A' + 26;
 36         if (c >= '0' && c <= '9') return c - '0' + 52;
 37     }
 38 
 39     void insert(char *s)
 40     {
 41         int n = strlen(s);
 42         int u = 0;
 43         for (int i = 0; i < n; i++)
 44         {
 45             int c = idx(s[i]);
 46             if (!ch[u][c])
 47             {
 48                 val[sz] = 0;
 49                 memset(ch[sz], 0, sizeof(ch[sz]));
 50                 ch[u][c] = sz++;
 51             }
 52             u = ch[u][c];
 53         }
 54         val[u] = 1;
 55     }
 56 
 57     void getfail()
 58     {
 59         queue<int> Q;
 60         Fail[0] = 0;
 61         for (int c = 0; c < SIGMA_SIZE; c++)
 62         {
 63             int u = ch[0][c];
 64             if (u)
 65             {
 66                 Fail[u] = 0; Q.push(u);
 67             }
 68         }
 69         while (!Q.empty())
 70         {
 71             int r = Q.front(); Q.pop();
 72             for (int c = 0; c < SIGMA_SIZE; c++)
 73             {
 74                 int u = ch[r][c];
 75                 if (!u)
 76                 {
 77                     ch[r][c] = ch[Fail[r]][c];
 78                     continue;
 79                 }
 80                 Q.push(u);
 81                 int v = Fail[r];
 82                 while (v && !ch[v][c]) v = Fail[v];
 83                 Fail[u] = ch[v][c];
 84                 val[u] |= val[Fail[u]];//注意有后缀为单词的情况
 85             }
 86         }
 87     }
 88 
 89     double solve(int u, int L)
 90     {
 91         if (vis[u][L]) return dp[u][L];
 92         vis[u][L] = true;
 93         if (!L) return dp[u][L] = 1;
 94         dp[u][L] = 0;
 95         for (int c = 0; c < SIGMA_SIZE; c++)
 96         {
 97             if (val[ch[u][c]]) continue;//如果是单词结尾或后缀是单词,跳过
 98             dp[u][L] += p[c] * solve(ch[u][c], L - 1);
 99         }
100         return dp[u][L];
101     }
102 };
103 
104 ACTree ac;
105 
106 int t, n, L;
107 char str[25];
108 
109 int main()
110 {
111     int Case = 0;
112     scanf("%d", &t);
113     while (t--)
114     {
115         ac.init();
116         scanf("%d", &n);
117         while (n--)
118         {
119             scanf("%s", str);
120             ac.insert(str);
121         }
122         ac.getfail();
123         scanf("%d", &n);
124         while (n--)
125         {
126             scanf("%s", str);
127             scanf("%lf", &ac.p[ac.idx(str[0])]);
128         }
129         scanf("%d", &L);
130         printf("Case #%d: %.6lf\n", ++Case, ac.solve(0, L));
131     }
132     return 0;
133 }
View Code

 3、uva 11019 Matrix Matcher

  题意:给出一个n*m的字符矩阵T,找出给定的x*y的字符矩阵P在T中出现了多少次.

  思路:

方法是利用count[i][j]来记录以(i,j)为左上角,且大小为x,y的矩形中含有多少个完整行与P矩阵对应行完全相同。

所以,如果在T矩阵的第r行的第c列开始与P的第i行匹配,那么count[r-i][c]++;

最后判断是否有count[][]==X.

  1 //假设N*M矩阵为T,X*Y矩阵为P
  2 //方法是利用count[i][j]来记录以(i, j)为左上角,且大小为X,Y的矩形中含有多少个完整行与P矩阵对应行完全相同。
  3 //所以,如果在T矩阵的第r行的第c列开始与P的第i行匹配,那么count[r - i][c]++;
  4 //最后判断是否有count[][] == X;
  5 
  6 #include<cstring>  
  7 #include<cstdio>  
  8 #include<queue>  
  9 #include<iostream>
 10 using namespace std;
 11 const int maxn = 100 * 100 + 10;
 12 const int sigma_size=26;
 13 
 14 int N, M, X, Y;
 15 char map[1005][1005];
 16 int cnt[1005][1005];
 17 
 18 struct AC_automation
 19 {
 20     struct node
 21     {
 22         int next[sigma_size];
 23         int val;//树节点的附加信息  
 24         int fail;//存储节点的失配指针  
 25         vector<int>line;//记录该单词所在的行数,可能多个行的单词相等  
 26         void init()
 27         {
 28             memset(next, 0, sizeof(next));
 29             line.clear();
 30             fail = val = 0;
 31         }
 32     }ch[maxn];//用来存储Trie树  
 33     int sz;//整棵树节点的总数   
 34     int idx(char c)
 35     {
 36         return c - 'a';     //对于小写字母集,获得c的编号  
 37     }
 38 
 39     void init()
 40     {
 41         sz = 1;
 42         ch[0].init();
 43     }
 44     void insert(char *s, int v)
 45     {
 46         int u = 0, len = strlen(s);
 47         for (int i = 0; i < len; i++)
 48         {
 49             int c = idx(s[i]);
 50             if (ch[u].next[c] == 0)
 51             {
 52                 ch[sz].init();
 53                 ch[u].next[c] = sz++;
 54             }
 55             u = ch[u].next[c];
 56         }
 57         ch[u].val = 1;
 58         ch[u].line.push_back(v);
 59     }
 60 
 61     //利用bfs得到失配指针fail  
 62     void get_fail()
 63     {
 64         queue<int>Q;
 65         for (int i = 0; i < sigma_size; i++)
 66             if (ch[0].next[i] != 0)
 67                 Q.push(ch[0].next[i]);
 68         while (!Q.empty())
 69         {
 70             int temp = Q.front();
 71             Q.pop();
 72             for (int i = 0; i < sigma_size; i++)
 73             {
 74                 if (ch[temp].next[i] != 0)
 75                 {
 76                     Q.push(ch[temp].next[i]);
 77                     int fail_temp = ch[temp].fail;
 78                     //关键步骤,如果fail指针所指的节点没有('a'+i)这个子节点,继续递归,直到fail_temp指向0节点,即根节点  
 79                     while (fail_temp > 0 && ch[fail_temp].next[i] == 0)
 80                         fail_temp = ch[fail_temp].fail;
 81 
 82                     if (ch[fail_temp].next[i] != 0)  //如果fail_temp节点的子节点有('a'+i)这个字符  
 83                         fail_temp = ch[fail_temp].next[i];
 84                     ch[ch[temp].next[i]].fail = fail_temp;   //子节点的失配节点由父节点节点决定  
 85                 }
 86             }
 87         }
 88     }
 89 
 90     //寻找text[]总共有多少个单词  
 91     void find(char *text, int x)
 92     {
 93         int len = strlen(text);
 94         int node = 0, fail_temp = 0;
 95         for (int i = 0; i < len; i++)
 96         {
 97             int character = idx(text[i]);
 98             while (fail_temp > 0 && ch[fail_temp].next[character] == 0)
 99                 //若没有text[i]这个字符,则使用失配指针继续在Trie树上遍历  
100                 fail_temp = ch[fail_temp].fail;
101 
102             if (ch[fail_temp].next[character] != 0)
103             { //此时找到了text[i]这个字符  
104                 fail_temp = ch[fail_temp].next[character];
105                 int fail_temp2 = fail_temp;//此处必须有另一个变量来记录失配的时候的下标  
106                 while (fail_temp2 > 0 && ch[fail_temp2].val)
107                 {
108                     for (int j = 0; j < ch[fail_temp2].line.size(); j++)
109                     {
110                         //枚举该单词所在的所有行  
111                         int temp = ch[fail_temp2].line[j];
112                         if (x >= temp)
113                             cnt[x - temp][i] ++;
114                     }
115                     fail_temp2 = ch[fail_temp2].fail;
116                 }
117             }
118         }
119     }
120 }AC;
121 
122 int main()
123 {
124     int T;
125     scanf("%d", &T);
126     while (T--)
127     {
128         AC.init();
129         memset(cnt, 0, sizeof(cnt));
130         scanf("%d%d", &N, &M);
131         for (int i = 0; i < N; i++)
132             scanf("%s", map[i]);
133         scanf("%d%d", &X, &Y);
134         char str_temp[105];
135         for (int i = 0; i < X; i++)
136         {
137             scanf("%s", str_temp);
138             AC.insert(str_temp, i);
139         }
140         AC.get_fail();
141         for (int i = 0; i < N; i++)
142         {
143             AC.find(map[i], i);
144         }
145         int ans = 0;
146         for (int i = 0; i < N; i++)
147         {
148             for (int j = 0; j < M; j++)
149             {
150                 if (cnt[i][j] == X)
151                     ans++;
152             }
153         }
154         printf("%d\n", ans);
155     }
156     return 0;
157 }
View Code

 

转载于:https://www.cnblogs.com/ivan-count/p/7498210.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值