AC自动机算法总结(未完待续)

 

      学习AC自动机之前,必须先得把字典树和KMP算法给理解透了,否则会让你吃尽苦头,切不可本末倒置。 

      参考文献: http://www.cs.uku.fi/~kilpelai/BSA05/lectures/slides04.pdf

 

  简单介绍一下AC自动机:

     1.必须得先建一颗字典树,这是构建AC自动的基本结构,好比建房子的地基。

     2.接下来就是AC自动机的构造了,里面最强大的就是fail指针(失败指针)了,fail指针的作用是当前字符匹配失败的时候字符跳转到下一个和此字符有最长公共前缀的字符,如果没有的话直接跳到根节点,且跳转必是深度深的向深度浅的跳转。 通过深度跳转的规律可知,我们可以利用bfs构建fail指针的状态转移图。

    3.最后就是直接通过扫描目标串进行匹配了。

 

一:AC自动机之多模式串匹配

     hdu2222  Keywords Search (入门题)

     题目大意:给你n个模式串,然后再给你一个目标串,问你目标串中出现了多少次模式串。

     解题思路:O(-1)

View Code
 1 #include <iostream>

 2 #include <cstdio>

 3 #include <cstring>

 4 #include <algorithm>

 5 using namespace std;

 6 

 7 const int maxn=250005;

 8 const int maxm=1000005;

 9 const int N=26;

10 char str[maxm];

11 int que[maxn], sz;

12 

13 struct node

14 {

15     int fail, next[N];

16     int count;

17     void init()

18     {

19         fill(next,next+26,0);

20         fail=count=0;

21     }

22 }tree[maxn];

23 

24 void insert(char *s)

25 {

26     int p=0;

27     while(*s)

28     {

29         int c=(*s++)-'a';

30         if(!tree[p].next[c])

31         {

32             tree[sz].init();

33             tree[p].next[c]=sz++;

34         }

35         p=tree[p].next[c];

36     }

37     tree[p].count++;

38 }

39 

40 void Creat_AC()

41 {

42     int head=0, tail=0, now, p, fell;

43     for(int i=0; i<N; i++)

44       if(tree[0].next[i])

45       {

46           que[tail++]=tree[0].next[i];

47           tree[tree[0].next[i]].fail=0;

48       }

49     while(head!=tail)

50     {

51         now=que[head++];

52         fell=tree[now].fail;

53         for(int i=0; i<N; i++)

54         {

55             if(p=tree[now].next[i])

56             {

57                 tree[p].fail=tree[fell].next[i];

58                 que[tail++]=p;

59             }

60             else tree[now].next[i]=tree[fell].next[i];

61         }

62     }

63 }

64 

65 int Search_AC(char *s)

66 {

67     int ans=0, p=0, c;

68     while(*s)

69     {

70         c=(*s++)-'a';

71         p=tree[p].next[c];

72         int tmp=p;

73         while(tmp&&tree[tmp].count!=-1)

74         {

75             ans+=tree[tmp].count;

76             tree[tmp].count=-1;

77             tmp=tree[tmp].fail;

78         }

79     }

80     return ans;

81 }

82 

83 int main()

84 {

85     int n, T;

86     cin >> T;

87     while(T--)

88     {

89         cin >> n;

90         tree[0].init();

91         sz=1;

92         for(int i=1; i<=n; i++) scanf("%s",str), insert(str);

93         Creat_AC();

94         getchar();

95         gets(str);

96         printf("%d\n",Search_AC(str));

97     }

98     return 0;

99 }

 

    hdu2896 病毒侵袭

    题目大意:给你n个不同的模式串,然后再给你m个目标串,问你这m个目标串中有多少个目标串含有模式串,含有模式串的目标串排序后输出模式串。

    解题思路:AC自动机模板题,但是做的时候还是出了一点小问题,因为这里出现了多个目标串,而我在询问匹配的时候不是进行标记改变了num值的大小,这样就导致了下次另外一个目标串再次询问到这里的时候就出问题了。只有一个目标串时改num值为-1是为了防止再次遍历相同的地方,深刻理解算法才是王道。

View Code
  1 #include <iostream>

  2 #include <cstdio>

  3 #include <cstring>

  4 #include <vector>

  5 #include <algorithm>

  6 using namespace std;

  7 

  8 const int maxn=100005;

  9 const int N=94;

 10 char str[maxn];

 11 int que[maxn], sz;

 12 int color[maxn];

 13 vector<int>vt;

 14 

 15 struct node

 16 {

 17     int fail,  num;

 18     int next[N];

 19     void init()

 20     {

 21         fill(next,next+N,0);

 22         fail=num=0;

 23     }

 24 } tree[maxn];

 25 

 26 void insert(char *s, int num)

 27 {

 28     int p=0;

 29     while(*s)

 30     {

 31         int c=(*s++)-' ';

 32         if(!tree[p].next[c])

 33         {

 34             tree[sz].init();

 35             tree[p].next[c]=sz++;

 36         }

 37         p=tree[p].next[c];

 38     }

 39     tree[p].num=num;

 40 }

 41 

 42 void Creat_AC()

 43 {

 44     int head=0, tail=0, now, p, fell;

 45     for(int i=0; i<N; i++)

 46         if(tree[0].next[i])

 47         {

 48             tree[tree[0].next[i]].fail=0;

 49             que[tail++]=tree[0].next[i];

 50         }

 51     while(head!=tail)

 52     {

 53         now=que[head++];

 54         fell=tree[now].fail;

 55         for(int i=0; i<N; i++)

 56         {

 57             if(p=tree[now].next[i])

 58             {

 59                 tree[p].fail=tree[fell].next[i];

 60                 que[tail++]=p;

 61             }

 62             else tree[now].next[i]=tree[fell].next[i];

 63         }

 64     }

 65 }

 66 

 67 bool Search_AC(char *s)

 68 {

 69     int p=0, c, flag=0;

 70     fill(color,color+sz,0);

 71     while(*s)

 72     {

 73         c=(*s++)-' ';

 74         p=tree[p].next[c];

 75         int tmp=p;

 76         while(tmp&&!color[tmp]&&tree[tmp].num)

 77         {

 78             flag=1;

 79             color[tmp]=1; ///开始没用标记符号,而是直接把tree[tmp].num改成0,其实这样是错误的,因为文本串不止一个,这样改的话就毁坏了所建的AC自动机

 80             vt.push_back(tree[tmp].num);

 81             tmp=tree[tmp].fail;

 82         }

 83     }

 84     return flag;

 85 }

 86 

 87 int main()

 88 {

 89     int n, m;

 90     while(cin >> n)

 91     {

 92         sz=1, tree[0].init();

 93         for(int i=1; i<=n; i++) scanf("%s",str), insert(str,i);

 94         Creat_AC();

 95         cin >> m;

 96         int sum=0;

 97         for(int i=1; i<=m; i++)

 98         {

 99             scanf("%s",str);

100             vt.clear();

101             if(Search_AC(str))

102             {

103                 sum++;

104                 printf("web %d:",i);

105                 sort(vt.begin(),vt.end());

106                 for(int j=0; j<vt.size(); j++)

107                     printf(" %d",vt[j]);

108                 puts("");

109             }

110         }

111         printf("total: %d\n",sum);

112     }

113     return 0;

114 }

 

   hdu3065  病毒侵袭持续中

   题目大意:多个模式串一个目标串,问你模式串在目标串中出现的次数。

   解题思路:O(-1)

View Code
 1 #include <iostream>

 2 #include <cstdio>

 3 #include <algorithm>

 4 #include <cstring>

 5 using namespace std;

 6 

 7 const int maxn= 50050;

 8 const int  maxm= 2000005;

 9 const int N= 94;

10 char ss[maxn][55];

11 char str[maxm];

12 int que[maxn], sz;

13 int mp[maxn];

14 

15 struct node

16 {

17     int num ,fail;

18     int next[N];

19     void init()

20     {

21         num=fail=0;

22         fill(next,next+N,0);

23     }

24 }tree[maxn];

25 

26 void insert(char *s, int id)

27 {

28     int p=0;

29     while(*s)

30     {

31         int c=(*s++)-' ';

32         if(!tree[p].next[c])

33         {

34             tree[sz].init();

35             tree[p].next[c]=sz++;

36         }

37         p=tree[p].next[c];

38     }

39     tree[p].num=id;

40 }

41 

42 void Creat_AC()

43 {

44     int head=0, tail=0, now, fell, p;

45     for(int i=0; i<N; i++)

46         if(tree[0].next[i])

47         {

48              que[tail++]=tree[0].next[i];

49              tree[tree[0].next[i]].fail=0;

50         }

51     while(head!=tail)

52     {

53         now=que[head++];

54         fell=tree[now].fail;

55         for(int i=0; i<N; i++)

56         {

57             if(p=tree[now].next[i])

58             {

59                 tree[p].fail=tree[fell].next[i];

60                 que[tail++]=p;

61             }

62             else tree[now].next[i]=tree[fell].next[i];

63         }

64     }

65 }

66 

67 void Search_AC(char *s)

68 {

69     fill(mp,mp+sz,0);

70     int p=0;

71     while(*s)

72     {

73         int c=(*s++)-' ';

74         p=tree[p].next[c];

75         int tmp=p;

76         while(tmp&&tree[tmp].num)

77         {

78             mp[tree[tmp].num]++;

79             tmp=tree[tmp].fail;

80         }

81     }

82 }

83 

84 int main()

85 {

86     int n;

87     while(cin >> n)

88     {

89         sz=1, tree[0].init();

90         for(int i=1; i<=n; i++) scanf("%s",ss[i]), insert(ss[i],i);

91         Creat_AC();

92         getchar();

93         gets(str);

94         Search_AC(str);

95         for(int i=1; i<=n; i++)

96             if(mp[i])  printf("%s: %d\n",ss[i],mp[i]);

97     }

98     return 0;

99 }

   

   hdu3695   Computer Virus on Planet Pandora

   题目大意:有n个模式串一个两个目标串(两个目标串分别为一个主串以及这个主串的倒置串),问你这两个目标串中出现了多少个不同的模式串。

   解题思路:模板题,需要注意的地方是目标串中出现了多少个不同的模式串,不标记的话会重复累加,比如:AABBCC, CCBBAA, 标记了只出现了3个不同的模式串,不标记则有6个,其余的基本直接上模板。

View Code
  1 #include <iostream>

  2 #include <cstdio>

  3 #include <algorithm>

  4 #include <cstring>

  5 using namespace std;

  6 

  7 const int maxn= 250050;

  8 const int  maxm= 5100005;

  9 const int N= 26;

 10 char  str[maxm];

 11 int que[maxn], sz;

 12 int mark[maxn], mp[maxn];

 13 

 14 struct node

 15 {

 16     int count ,fail;

 17     int next[N];

 18     void init()

 19     {

 20         count=fail=0;

 21         fill(next,next+N,0);

 22     }

 23 }tree[maxn];

 24 

 25 void insert(char *s, int id)

 26 {

 27     int p=0;

 28     while(*s)

 29     {

 30         int c=(*s++)-'A';

 31         if(!tree[p].next[c])

 32         {

 33             tree[sz].init();

 34             tree[p].next[c]=sz++;

 35         }

 36         p=tree[p].next[c];

 37     }

 38     tree[p].count=id;

 39 }

 40 

 41 void Creat_AC()

 42 {

 43     int head=0, tail=0, now, fell, p;

 44     for(int i=0; i<N; i++)

 45         if(tree[0].next[i])

 46         {

 47              que[tail++]=tree[0].next[i];

 48              tree[tree[0].next[i]].fail=0;

 49         }

 50     while(head!=tail)

 51     {

 52         now=que[head++];

 53         fell=tree[now].fail;

 54         for(int i=0; i<N; i++)

 55         {

 56             if(p=tree[now].next[i])

 57             {

 58                 tree[p].fail=tree[fell].next[i];

 59                 que[tail++]=p;

 60             }

 61             else tree[now].next[i]=tree[fell].next[i];

 62         }

 63     }

 64 }

 65 

 66 int Search_AC(string  s)

 67 {

 68     int p=0, i=0, ans=0;

 69     fill(mark,mark+sz,0);

 70     while(i<s.size())

 71     {

 72         int c=s[i]-'A';

 73         p=tree[p].next[c];

 74         int tmp=p;

 75         while(tmp&&!mark[tmp])

 76         {

 77             mp[tree[tmp].count]=1;

 78             mark[tmp]=1;

 79             tmp=tree[tmp].fail;

 80         }

 81         i++;

 82     }

 83     return ans;

 84 }

 85 

 86 int main()

 87 {

 88     int n, T;

 89     cin >> T;

 90     while(T--)

 91     {

 92         cin >> n;

 93         sz=1, tree[0].init();

 94         for(int i=1; i<=n; i++) scanf("%s",str), insert(str,i);

 95         Creat_AC();

 96         scanf("%s",str);

 97         string s="";

 98         int len=strlen(str);

 99         int i=0;

100         while(i<len)

101         {

102             if(str[i]=='[')

103             {

104                 int tp=0;

105                 for(int j=i+1; j<len; j++)

106                     if('0'<=str[j]&&str[j]<='9') tp=tp*10+str[j]-'0';

107                     else

108                     {

109                         while(tp--) s+=str[j];

110                         i=j+2;

111                         break;

112                     }

113             }

114             else s+=str[i], i++;

115         }

116         int ans=0;

117         fill(mp,mp+n+1,0);   ///!!这题要注意的就是两个主串可能有相同的子串(如ABBC BB都正反序列都出现了),不标记的话会重复累加

118         Search_AC(s);

119         reverse(s.begin(),s.end());

120         Search_AC(s);

121         for(int i=1; i<=n; i++) ans+=mp[i];

122         cout << ans <<endl;

123     }

124     return 0;

125 }

  zoj3430  Detect the Virus

  题目大意:O(-1)。

  解题思路 :解码+AC自动机模板

View Code
  1 #include <iostream>

  2 #include <cstdio>

  3 #include <algorithm>

  4 #include <cstring>

  5 using namespace std;

  6 

  7 const int maxn= 50010;

  8 const int N= 257;

  9 char  str[maxn];

 10 int que[maxn], sz;

 11 int mark[maxn];

 12 

 13 char pp[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="};

 14 int fp[N];

 15 char bite[65][7]={

 16 "000000",

 17 "000001","000010","000011","000100","000101","000110","000111","001000",

 18 "001001","001010","001011","001100","001101","001110","001111","010000",

 19 "010001","010010","010011","010100","010101","010110","010111","011000",

 20 "011001","011010","011011","011100","011101","011110","011111","100000",

 21 "100001","100010","100011","100100","100101","100110","100111","101000",

 22 "101001","101010","101011","101100","101101","101110","101111","110000",

 23 "110001","110010","110011","110100","110101","110110","110111","111000",

 24 "111001","111010","111011","111100","111101","111110","111111"};

 25 

 26 struct node

 27 {

 28     int num, fail;

 29     int next[N];

 30     void init()

 31     {

 32         num=fail=0;

 33         memset(next,0,sizeof(next));

 34     }

 35 }tree[maxn];

 36 

 37 int change(char *s, int *f)

 38 {

 39     string ss="";

 40     int len=strlen(s);

 41     for(int i=0; i<len; i++)

 42     {

 43         if(s[i]=='=') break;

 44         else ss+=bite[fp[s[i]]];

 45     }

 46     int ep=0, m=ss.size()/8;

 47     for(int i=0; i<8*m; i+=8)

 48     {

 49         int tp=1, sum=0;

 50         for(int j=i+7; j>=i; j--)

 51             sum+=tp*(ss[j]-'0'),tp*=2;

 52         f[ep++]=(unsigned char)sum;

 53     }

 54     f[ep]=0;

 55     return ep;

 56 }

 57 

 58 void insert(int *s, int len, int id)

 59 {

 60     int p=0, i=0;

 61     while(i<len)

 62     {

 63         if(!tree[p].next[s[i]])

 64         {

 65             tree[sz].init();

 66             tree[p].next[s[i]]=sz++;

 67         }

 68         p=tree[p].next[s[i]];

 69         i++;

 70     }

 71     tree[p].num=id;

 72 }

 73 

 74 void Creat_AC()

 75 {

 76     int head=0, tail=0, now, p, fell;

 77     for(int i=0; i<N; i++)

 78         if(tree[0].next[i])

 79         {

 80             que[tail++]=tree[0].next[i];

 81             tree[tree[0].next[i]].fail=0;

 82         }

 83     while(head!=tail)

 84     {

 85         now=que[head++];

 86         fell=tree[now].fail;

 87         for(int i=0; i<N; i++)

 88         {

 89             if(p=tree[now].next[i])

 90             {

 91                 tree[p].fail=tree[fell].next[i];

 92                 que[tail++]=p;

 93             }

 94             else tree[now].next[i]=tree[fell].next[i];

 95         }

 96     }

 97 }

 98 

 99 int Search_AC(int *s, int len)

100 {

101     fill(mark,mark+sz,0);

102     int p=0, ans=0, i=0;

103     while(i<len)

104     {

105         p=tree[p].next[s[i]];

106         int tmp=p;

107        // cout << p <<endl;

108         while(tmp&&!mark[tmp])

109         {

110             mark[tmp]=1;

111             if(tree[tmp].num) ans++;

112             tmp=tree[tmp].fail;

113         }

114         i++;

115     }

116     return ans;

117 

118 }

119 

120 int main()

121 {

122     int n, m;

123     int tmp[3000];

124     for(int i=0; i<=64; i++)

125         fp[pp[i]]=i;

126     while(cin >> n)

127     {

128         sz=1, tree[0].init();

129         for(int i=1; i<=n; i++)

130         {

131             scanf("%s",str);

132             int len=change(str,tmp);

133             insert(tmp,len,i);

134         }

135         Creat_AC();

136         cin >> m;

137         for(int i=1; i<=m; i++)

138         {

139             scanf("%s",str);

140             int len=change(str,tmp);

141             int ans=Search_AC(tmp,len);

142             printf("%d\n",ans);

143         }

144         puts("");

145     }

146     return 0;

147 }

 

二、AC自动机+矩阵(先占坑)

     pku 2778 DNA Sequence

     HDU2243  考研路茫茫——单词情结

三、AC自动机+DP(先占坑)

     pku 3691 DNA repair

     HDU 2825 Wireless Password

     pku 1625 Censored!

 

 

你可能感兴趣的:(AC自动机)