zoukankan      html  css  js  c++  java
  • 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
    ", best);
    140         for (int i = 1; i <= n; i++)
    141             if (ac.cnt[ms[string(P[i])]] == best) printf("%s
    ", 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
    ", ++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
    ", ans);
    155     }
    156     return 0;
    157 }
    View Code
  • 相关阅读:
    教会他人,让其成为你的接棒人
    2015年看的52部电影计划
    我的2015年读书计划,每两周读完一本书!
    拯救你的电脑之文件命名规范与目录规划
    出租WiFi到底靠不靠谱?
    使用观察者模式更新Fragment的内容
    android静默安装和智能安装(转)
    Android拨打电话不弹出系统拨号界面总结
    Android通过AIDL和反射调用系统拨打电话和挂断电话
    Android设为系统默认的短信应用
  • 原文地址:https://www.cnblogs.com/ivan-count/p/7498210.html
Copyright © 2011-2022 走看看