layout: post

title: 「kuangbin带你飞」专题十七 AC自动机

author: "luowentaoaa"

catalog: true

tags:

- kuangbin

- 字符串

- AC自动机


传送门

A.HDU2222 Keywords Search

模板题。给出N个单词,后给你一个长串,问长串中有几个单词。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const ll mod=998244353;
  5. const int maxn=1e6+50;
  6. const ll inf=0x3f3f3f3f3f3f;
  7. const int maxm=500010;
  8. struct Trie{
  9. int next[maxm][26],fail[maxm],end[maxm];
  10. int root,L;
  11. int newnode(){
  12. for(int i=0;i<26;i++)
  13. next[L][i]=-1;
  14. end[L++]=0;
  15. return L-1;
  16. }
  17. void init(){
  18. L=0;
  19. root=newnode();
  20. }
  21. void insert(char buf[]){
  22. int len=strlen(buf);
  23. int now=root;
  24. for(int i=0;i<len;i++){
  25. if(next[now][buf[i]-'a']==-1)
  26. next[now][buf[i]-'a']=newnode();
  27. now=next[now][buf[i]-'a'];
  28. }
  29. end[now]++;
  30. }
  31. void build(){
  32. queue<int>Q;
  33. fail[root]=root;
  34. for(int i=0;i<26;i++)
  35. if(next[root][i]==-1)
  36. next[root][i]=root;
  37. else{
  38. fail[next[root][i]]=root;
  39. Q.push(next[root][i]);
  40. }
  41. while(!Q.empty()){
  42. int now=Q.front();
  43. Q.pop();
  44. for(int i=0;i<26;i++)
  45. if(next[now][i]==-1)
  46. next[now][i]=next[fail[now]][i];
  47. else{
  48. fail[next[now][i]]=next[fail[now]][i];
  49. Q.push(next[now][i]);
  50. }
  51. }
  52. }
  53. int query(char buf[]){
  54. int len=strlen(buf);
  55. int now=root;
  56. int res=0;
  57. for(int i=0;i<len;i++){
  58. now=next[now][buf[i]-'a'];
  59. int temp=now;
  60. while(temp!=root){
  61. res+=end[temp];
  62. end[temp]=0;
  63. temp=fail[temp];
  64. }
  65. }
  66. return res;
  67. }
  68. void debug(){
  69. for(int i=0;i<L;i++){
  70. printf("id =%3d,fail = %3d,end = %3d ,chi = [",i,fail[i],end[i]);
  71. for(int j=0;j<26;j++)
  72. printf("%2d",next[i][j]);
  73. printf("]\n");
  74. }
  75. }
  76. };
  77. char buf[maxn];
  78. Trie ac;
  79. int main()
  80. {
  81. std::ios::sync_with_stdio(false);
  82. std::cin.tie(0);
  83. int t;
  84. cin>>t;
  85. while(t--){
  86. int n;
  87. cin>>n;
  88. ac.init();
  89. for(int i=0;i<n;i++){
  90. cin>>buf;
  91. ac.insert(buf);
  92. }
  93. ac.build();
  94. cin>>buf;
  95. cout<<ac.query(buf)<<endl;
  96. }
  97. return 0;
  98. }

B.HDU2896 病毒侵袭

和A题一样的模板题,不过要求我们计算长串中出现了那些字符串,在ac自动机的end数组改成是哪一个字符串的编号然后在查询的时候用used数组记录一下既可。

注意是字符串不仅仅包括字母所以数组开开到128

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const ll mod=998244353;
  5. const int maxn=1e6+50;
  6. const ll inf=0x3f3f3f3f3f3f;
  7. const int maxm=500010;
  8. struct Trie{
  9. int next[maxm][128],fail[maxm],end[maxm];
  10. bool used[550];
  11. int root,L;
  12. int newnode(){
  13. for(int i=0;i<128;i++)
  14. next[L][i]=-1;
  15. end[L++]=0;
  16. return L-1;
  17. }
  18. void init(){
  19. L=0;
  20. root=newnode();
  21. }
  22. void insert(char buf[],int id){
  23. int len=strlen(buf);
  24. int now=root;
  25. for(int i=0;i<len;i++){
  26. if(next[now][buf[i]]==-1)
  27. next[now][buf[i]]=newnode();
  28. now=next[now][buf[i]];
  29. }
  30. end[now]=id;
  31. }
  32. void build(){
  33. queue<int>Q;
  34. fail[root]=root;
  35. for(int i=0;i<128;i++)
  36. if(next[root][i]==-1)
  37. next[root][i]=root;
  38. else{
  39. fail[next[root][i]]=root;
  40. Q.push(next[root][i]);
  41. }
  42. while(!Q.empty()){
  43. int now=Q.front();
  44. Q.pop();
  45. for(int i=0;i<128;i++)
  46. if(next[now][i]==-1)
  47. next[now][i]=next[fail[now]][i];
  48. else{
  49. fail[next[now][i]]=next[fail[now]][i];
  50. Q.push(next[now][i]);
  51. }
  52. }
  53. }
  54. bool query(char buf[],int n,int id){
  55. int len=strlen(buf);
  56. int now=root;
  57. int res=0;
  58. for(int i=0;i<=n;i++)used[i]=false;
  59. bool flag=false;
  60. for(int i=0;i<len;i++){
  61. now=next[now][buf[i]];
  62. int temp=now;
  63. while(temp!=root){
  64. if(end[temp]){
  65. used[end[temp]]=true;
  66. flag=true;
  67. }
  68. temp=fail[temp];
  69. }
  70. }
  71. if(!flag)return false;
  72. else{
  73. cout<<"web "<<id<<":";
  74. for(int i=1;i<=n;i++)
  75. if(used[i])cout<<" "<<i;
  76. cout<<endl;
  77. return true;
  78. }
  79. }
  80. void debug(){
  81. for(int i=0;i<L;i++){
  82. printf("id =%3d,fail = %3d,end = %3d ,chi = [",i,fail[i],end[i]);
  83. for(int j=0;j<26;j++)
  84. printf("%2d",next[i][j]);
  85. printf("]\n");
  86. }
  87. }
  88. };
  89. char buf[maxn];
  90. Trie ac;
  91. int main()
  92. {
  93. std::ios::sync_with_stdio(false);
  94. std::cin.tie(0);
  95. int t;
  96. int n;
  97. cin>>n;
  98. ac.init();
  99. for(int i=1;i<=n;i++){
  100. cin>>buf;
  101. ac.insert(buf,i);
  102. }
  103. int m;
  104. ac.build();
  105. cin>>m;
  106. int sum=0;
  107. for(int i=1;i<=m;i++){
  108. cin>>buf;
  109. if(ac.query(buf,n,i))sum++;
  110. }
  111. cout<<"total: "<<sum<<endl;
  112. return 0;
  113. }

C.HDU3065 病毒侵袭持续中

注意:这题是多组数据,但是题面上没写很坑~!

这题和B题一样的题意只不过题面很坑

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const ll mod=998244353;
  5. const int maxn=1e8+50;
  6. const ll inf=0x3f3f3f3f3f3f;
  7. const int maxm=500010;
  8. char word[1100][55];
  9. struct Trie{
  10. int next[maxm][128],fail[maxm],end[maxm];
  11. int used[1100];
  12. int root,L;
  13. int newnode(){
  14. for(int i=0;i<128;i++)
  15. next[L][i]=-1;
  16. end[L++]=0;
  17. return L-1;
  18. }
  19. void init(){
  20. L=0;
  21. root=newnode();
  22. }
  23. void insert(char buf[],int id){
  24. int len=strlen(buf);
  25. int now=root;
  26. for(int i=0;i<len;i++){
  27. if(next[now][buf[i]]==-1)
  28. next[now][buf[i]]=newnode();
  29. now=next[now][buf[i]];
  30. }
  31. end[now]=id;
  32. }
  33. void build(){
  34. queue<int>Q;
  35. fail[root]=root;
  36. for(int i=0;i<128;i++)
  37. if(next[root][i]==-1)
  38. next[root][i]=root;
  39. else{
  40. fail[next[root][i]]=root;
  41. Q.push(next[root][i]);
  42. }
  43. while(!Q.empty()){
  44. int now=Q.front();
  45. Q.pop();
  46. for(int i=0;i<128;i++)
  47. if(next[now][i]==-1)
  48. next[now][i]=next[fail[now]][i];
  49. else{
  50. fail[next[now][i]]=next[fail[now]][i];
  51. Q.push(next[now][i]);
  52. }
  53. }
  54. }
  55. bool query(char buf[],int n){
  56. int len=strlen(buf);
  57. int now=root;
  58. int res=0;
  59. for(int i=0;i<=n;i++)used[i]=0;
  60. for(int i=0;i<len;i++){
  61. now=next[now][buf[i]];
  62. int temp=now;
  63. while(temp!=root){
  64. if(end[temp]){
  65. used[end[temp]]++;
  66. }
  67. temp=fail[temp];
  68. }
  69. }
  70. for(int i=1;i<=n;i++){
  71. if(used[i]){
  72. cout<<word[i]<<": "<<used[i]<<endl;
  73. }
  74. }
  75. }
  76. void debug(){
  77. for(int i=0;i<L;i++){
  78. printf("id =%3d,fail = %3d,end = %3d ,chi = [",i,fail[i],end[i]);
  79. for(int j=0;j<128;j++)
  80. printf("%2d",next[i][j]);
  81. printf("]\n");
  82. }
  83. }
  84. };
  85. char buf[maxn];
  86. Trie ac;
  87. int main()
  88. {
  89. std::ios::sync_with_stdio(false);
  90. std::cin.tie(0);
  91. int t;
  92. int n;
  93. while(cin>>n){
  94. ac.init();
  95. for(int i=1;i<=n;i++){
  96. cin>>word[i];
  97. ac.insert(word[i],i);
  98. }
  99. int m;
  100. ac.build();
  101. // ac.debug();
  102. cin>>buf;
  103. ac.query(buf,n);
  104. }
  105. return 0;
  106. }

D.ZOJ3430 Detect the Virus

题意

给你N个病毒(大小不超过64bytes子串)然后给你M个长串(大小不超过2048byte)问你长串没处理中有几个没处理的子串;

注意:
这里的子串和长串都是经过题目要求处理后的串
处理要求是原本的串的每个字符都是二进制长度为八位,然后合并在一起六位二进制组成一个新的字符,如果末尾缺了两位就添加一个'='字符
例如:假设ab的八位二进制为10101000 01100101
现在合并成101010 000110 0101
变成(F,P,E)
因为后面的0101缺少了两位所以补一个'='所以最终字符串为FPE=;
#### 解法
这题的难点在于**把字符串根据题目要求模拟成变化前的样子**,然后就是AC自动机板子题;
我们可以把开始的字符串去掉'='然后把字符串的每个字符变成数字(这样好变成二进制的数字处理)然后把
1.第一个数字取后面六位,第二个数字取前面两位;

2.第二个数字取后面四位,第三个数字取前面四位;

3.第三个数字取后面两位,第四个数字取前面六位;

这样就四个数字一组组成三个八位的数字;

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const ll mod=998244353;
  5. const int maxn=2048*10;
  6. const ll inf=0x3f3f3f3f3f3f;
  7. const int maxm=512*64;
  8. struct Trie{
  9. int next[maxm][260],fail[maxm],end[maxm];
  10. int used[1100];
  11. int root,L;
  12. int newnode(){
  13. for(int i=0;i<260;i++)
  14. next[L][i]=-1;
  15. end[L++]=0;
  16. return L-1;
  17. }
  18. void init(){
  19. L=0;
  20. root=newnode();
  21. }
  22. void insert(int buf[],int id,int len){
  23. //int len=strlen(buf);
  24. int now=root;
  25. for(int i=0;i<len;i++){
  26. if(next[now][buf[i]]==-1)
  27. next[now][buf[i]]=newnode();
  28. now=next[now][buf[i]];
  29. }
  30. end[now]=id;
  31. }
  32. void build(){
  33. queue<int>Q;
  34. fail[root]=root;
  35. for(int i=0;i<260;i++)
  36. if(next[root][i]==-1)
  37. next[root][i]=root;
  38. else{
  39. fail[next[root][i]]=root;
  40. Q.push(next[root][i]);
  41. }
  42. while(!Q.empty()){
  43. int now=Q.front();
  44. Q.pop();
  45. for(int i=0;i<260;i++)
  46. if(next[now][i]==-1)
  47. next[now][i]=next[fail[now]][i];
  48. else{
  49. fail[next[now][i]]=next[fail[now]][i];
  50. Q.push(next[now][i]);
  51. }
  52. }
  53. }
  54. void query(int buf[],int n,int len){
  55. //int len=strlen(buf);
  56. int now=root;
  57. int res=0;
  58. for(int i=0;i<=n;i++)used[i]=0;
  59. for(int i=0;i<len;i++){
  60. now=next[now][buf[i]];
  61. int temp=now;
  62. while(temp!=root){
  63. if(end[temp]){
  64. used[end[temp]]++;
  65. }
  66. temp=fail[temp];
  67. }
  68. }
  69. int sum=0;
  70. for(int i=1;i<=n;i++){
  71. if(used[i]){
  72. sum++;
  73. }
  74. }
  75. cout<<sum<<endl;
  76. }
  77. void debug(){
  78. for(int i=0;i<L;i++){
  79. printf("id =%3d,fail = %3d,end = %3d ,chi = [",i,fail[i],end[i]);
  80. for(int j=0;j<260;j++)
  81. printf("%2d",next[i][j]);
  82. printf("]\n");
  83. }
  84. }
  85. };
  86. int fun(char ch){
  87. if(ch>='A'&&ch<='Z')return ch-'A';
  88. if(ch>='a'&&ch<='z')return ch-'a'+26;
  89. if(ch>='0'&&ch<='9')return ch-'0'+52;
  90. if(ch=='+')return 62;
  91. if(ch=='/')return 63;
  92. }
  93. char buf[maxn];
  94. int aa[maxn];
  95. int after[maxn];
  96. int gao(int n){
  97. int t=0; ///00011010->01101000
  98. for(int i=0;i<n;i+=4){ ///00000110->00000000 按照题意合并成八位的01101000
  99. aa[t++]=((after[i]<<2)|(after[i+1]>>4));///取前一个的后六位 和后一个数的前两位 组成一个新的八位字符
  100. ///因为前面i把i+1的前两个取走了,所有i+1现在只有六个中的后四个和i+2的前四个匹配;
  101. if(i+2<n)aa[t++]=((after[i+1]<<4&0xff)|(after[i+2]>>2));
  102. ///因为前面i+1把i+2的前四个取走了,所有i+2现在只有六个中的两个和i+2的前六个匹配;
  103. if(i+3<n)aa[t++]=((after[i+2]<<6&0xff)|(after[i+3]));
  104. }
  105. return t;
  106. }
  107. Trie ac;
  108. int main()
  109. {
  110. std::ios::sync_with_stdio(false);
  111. std::cin.tie(0);
  112. std::cout.tie(0);
  113. int t;
  114. int n;
  115. while(cin>>n){
  116. ac.init();
  117. //cout<<"haha"<<endl;
  118. for(int i=1;i<=n;i++){
  119. cin>>buf;
  120. // cout<<"here:?"<<endl;
  121. int len=strlen(buf);
  122. while(buf[len-1]=='=')len--;
  123. for(int j=0;j<len;j++)after[j]=fun(buf[j]);
  124. int k=gao(len);
  125. ac.insert(aa,i,k);
  126. }
  127. int m;
  128. ac.build();
  129. cin>>m;
  130. while(m--){
  131. cin>>buf;
  132. int len=strlen(buf);
  133. while(buf[len-1]=='=')len--;
  134. for(int j=0;j<len;j++)after[j]=fun(buf[j]);
  135. int k=gao(len);
  136. ac.query(aa,n,k);
  137. }
  138. cout<<endl;
  139. }
  140. return 0;
  141. }

E.POJ2778 DNA Sequence

题意

给你N个病毒,问你有长度为L的DNA中有多少种是没有病毒的

思路

所有病毒串构造一个AC自动机,这个AC自动机可以看作一张有向图,图上的每个顶点就是Trie树上的结点,每个结点都可以看作是某个病毒串的前缀,Trie树的根则是空字符串。

而从根出发,在AC自动机上跑,经过k次转移到达某个结点,这个结点所代表的病毒串前缀可以看作长度为k的字符串的后缀,如果接下去跑往ATCG四个方向转移,就能到达新的结点,转移到新的长k+1字符串的后缀。

这样带着一个后缀状态的转移就能绕开病毒串,所以病毒串末尾的结点要标记,后缀存在病毒串的结点也要标记(这个在计算结点fail的时候就能处理),转移时就不能转移到被标记的结点。

接下来,题目的数据范围是10个长度10的病毒串,所以Trie树中最多101左右个结点,那么AC自动机整个转移就可以构建一张101*101的邻接矩阵,矩阵i行j列的权值是结点i转移到结点j的方案数。

而进行k次转移,从结点i转移到结点j的方案数是这个矩阵的k次幂,这个结论离散数学的图论有..

所以,长度n的字符串的方案数,就是转移n次根结点能到所有结点的方案和就是答案。就是计算矩阵的n次幂,统计根所在行的数字和,n的达到20亿用矩阵快速幂即可。

  1. 对于agcc而言,如果我zou a-g-c-d 这个路径。
  2. root
  3. / \
  4. a c
  5. /
  6. g
  7. /
  8. c
  9. /
  10. d
  11. 由上面这个图可知 左边的d 右边的c都是危险节点。 但漏掉了左边上的c
  12. 所以如果fail指针指向那个节点是危险节点的话,那么当前节点也是危险节点
  13. #include<algorithm>
  14. #include <iostream>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <stdio.h>
  18. #include <math.h>
  19. #include <time.h>
  20. #include <vector>
  21. #include <bitset>
  22. #include <queue>
  23. #include <map>
  24. #include <set>
  25. using namespace std;
  26. typedef long long ll;
  27. const ll mod=100000;
  28. const int maxn=10*10+5;
  29. const ll inf=0x3f3f3f3f3f3f3f3fLL;
  30. struct Matrix{
  31. unsigned long long mat[maxn][maxn];
  32. int n;
  33. Matrix(){}
  34. Matrix(int _n){
  35. n=_n;
  36. for(int i=0;i<n;i++)for(int j=0;j<n;j++)mat[i][j]=0;
  37. }
  38. Matrix operator *(const Matrix &b)const{
  39. Matrix ret=Matrix(n);
  40. for(int i=0;i<n;i++){
  41. for(int j=0;j<n;j++){
  42. for(int k=0;k<n;k++){
  43. ret.mat[i][j]+=(mat[i][k]*b.mat[k][j])%mod;
  44. ret.mat[i][j]%=mod;
  45. }
  46. }
  47. }
  48. return ret;
  49. }
  50. unsigned long long pow_m(unsigned long long a,int n){
  51. unsigned long long ret=1,tmp=a;
  52. while(n){
  53. if(n&1)ret*=tmp;
  54. tmp*=tmp;
  55. n>>=1;
  56. }return ret;
  57. }
  58. Matrix pow_M(Matrix a,ll n){
  59. Matrix ret=Matrix(a.n);
  60. for(int i=0;i<a.n;i++)ret.mat[i][i]=1;
  61. Matrix tmp=a;
  62. while(n){
  63. if(n&1)ret=ret*tmp;
  64. tmp=tmp*tmp;
  65. n>>=1;
  66. }
  67. return ret;
  68. }
  69. };
  70. struct Trie{
  71. int next[maxn][4],fail[maxn],id['Z'+1];
  72. bool end[maxn];
  73. int root,L;
  74. int newnode(){
  75. for(int i=0;i<4;++i)next[L][i]=-1;
  76. end[L++]=false;
  77. return L-1;
  78. }
  79. void init(){
  80. L=0;
  81. id['A']=0;id['T']=1;id['C']=2;id['G']=3;
  82. root=newnode();
  83. }
  84. void insert(char buf[]){
  85. int len=strlen(buf);
  86. int now=root;
  87. for(int i=0;i<len;i++){
  88. if(next[now][id[buf[i]]]==-1)next[now][id[buf[i]]]=newnode();
  89. now=next[now][id[buf[i]]];
  90. }
  91. end[now]=true;
  92. }
  93. void build(){
  94. queue<int>Q;
  95. fail[root]=root;
  96. for(int i=0;i<4;i++)
  97. if(next[root][i]==-1)next[root][i]=root;
  98. else{
  99. fail[next[root][i]]=root;
  100. Q.push(next[root][i]);
  101. }
  102. while(!Q.empty()){
  103. int now=Q.front();
  104. Q.pop();
  105. if(end[fail[now]])end[now]=true;
  106. for(int i=0;i<4;i++)
  107. if(next[now][i]==-1)
  108. next[now][i]=next[fail[now]][i];
  109. else{
  110. fail[next[now][i]]=next[fail[now]][i];
  111. Q.push(next[now][i]);
  112. }
  113. }
  114. }
  115. Matrix getMatrix(){
  116. Matrix ret=Matrix(L);
  117. for(int i=0;i<L;i++){
  118. for(int j=0;j<4;j++){
  119. if(end[next[i][j]]==false&&end[i]==false)
  120. ret.mat[i][next[i][j]]++;
  121. }
  122. }
  123. return ret;
  124. }
  125. };
  126. Trie ac;
  127. char buf[15];
  128. int main()
  129. {
  130. std::ios::sync_with_stdio(false);
  131. std::cin.tie(0);
  132. std::cout.tie(0);
  133. int n;
  134. ll L;
  135. cin>>n>>L;
  136. ac.init();
  137. for(int i=0;i<n;i++){
  138. cin>>buf;
  139. ac.insert(buf);
  140. }
  141. ac.build();
  142. Matrix mat=ac.getMatrix();
  143. mat=mat.pow_M(mat,L);
  144. ll ans=0;
  145. for(int i=0;i<mat.n;i++){
  146. ans=(ans+mat.mat[0][i])%mod;
  147. }
  148. cout<<ans<<endl;
  149. return 0;
  150. }

F.HDU2243 考研路茫茫――单词情结

题意

跟上题一样,不过这题是让你求包括的个数,而且不仅仅是长度为L而且长度小于L的也要包括进去;可以通过上题很快求出不包括的,然后求和,在求出全部的261+...26L的个数减去不包括的就是答案;

思路

通过矩阵求出全部长度的可能和AC自动机求出来的矩阵的和,然后相减

  1. #include<algorithm>
  2. #include <iostream>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <math.h>
  7. #include <time.h>
  8. #include <vector>
  9. #include <bitset>
  10. #include <queue>
  11. #include <map>
  12. #include <set>
  13. using namespace std;
  14. typedef long long ll;
  15. typedef unsigned long long ull;
  16. const ll mod=2147483648LL;
  17. const ll maxn=5*6+50;
  18. const ll inf=0x3f3f3f3f3f3f3f3fLL;
  19. struct Matrix{
  20. unsigned long long mat[maxn][maxn];
  21. int n;
  22. Matrix(){}
  23. Matrix(int _n){
  24. n=_n;
  25. for(int i=0;i<n;i++)for(int j=0;j<n;j++)mat[i][j]=0;
  26. }
  27. Matrix operator *(const Matrix &b)const{
  28. Matrix ret=Matrix(n);
  29. for(int i=0;i<n;i++){
  30. for(int j=0;j<n;j++){
  31. for(int k=0;k<n;k++){
  32. ret.mat[i][j]+=(mat[i][k]*b.mat[k][j]);
  33. }
  34. }
  35. }
  36. return ret;
  37. }
  38. unsigned long long pow_m(unsigned long long a,int n){
  39. unsigned long long ret=1,tmp=a;
  40. while(n){
  41. if(n&1)ret*=tmp;
  42. tmp*=tmp;
  43. n>>=1;
  44. }return ret;
  45. }
  46. Matrix pow_M(Matrix a,ll n){
  47. Matrix ret=Matrix(a.n);
  48. for(int i=0;i<a.n;i++)ret.mat[i][i]=1;
  49. Matrix tmp=a;
  50. while(n){
  51. if(n&1)ret=ret*tmp;
  52. tmp=tmp*tmp;
  53. n>>=1;
  54. }
  55. return ret;
  56. }
  57. };
  58. struct Trie{
  59. int next[maxn][26],fail[maxn];
  60. bool end[maxn];
  61. int root,L;
  62. int newnode(){
  63. for(int i=0;i<26;++i)next[L][i]=-1;
  64. end[L++]=false;
  65. return L-1;
  66. }
  67. void init(){
  68. L=0;
  69. root=newnode();
  70. }
  71. void insert(char buf[]){
  72. int len=strlen(buf);
  73. int now=root;
  74. for(int i=0;i<len;i++){
  75. if(next[now][buf[i]-'a']==-1)next[now][buf[i]-'a']=newnode();
  76. now=next[now][buf[i]-'a'];
  77. }
  78. end[now]=true;
  79. }
  80. void build(){
  81. queue<int>Q;
  82. fail[root]=root;
  83. for(int i=0;i<26;i++)
  84. if(next[root][i]==-1)next[root][i]=root;
  85. else{
  86. fail[next[root][i]]=root;
  87. Q.push(next[root][i]);
  88. }
  89. while(!Q.empty()){
  90. int now=Q.front();
  91. Q.pop();
  92. if(end[fail[now]])end[now]=true;
  93. for(int i=0;i<26;i++)
  94. if(next[now][i]==-1)
  95. next[now][i]=next[fail[now]][i];
  96. else{
  97. fail[next[now][i]]=next[fail[now]][i];
  98. Q.push(next[now][i]);
  99. }
  100. }
  101. }
  102. Matrix getMatrix(){
  103. Matrix ret=Matrix(L+1);
  104. for(int i=0;i<L;i++){
  105. for(int j=0;j<26;j++){
  106. if(end[next[i][j]]==false)
  107. ret.mat[i][next[i][j]]++;
  108. }
  109. }
  110. for(int i=0;i<L+1;i++)ret.mat[i][L]=1;
  111. return ret;
  112. }
  113. };
  114. Trie ac;
  115. char buf[15];
  116. int main()
  117. {
  118. std::ios::sync_with_stdio(false);
  119. std::cin.tie(0);
  120. std::cout.tie(0);
  121. int n;
  122. ll L;
  123. while(cin>>n>>L){
  124. ac.init();
  125. for(int i=0;i<n;i++){
  126. cin>>buf;
  127. ac.insert(buf);
  128. }
  129. ac.build();
  130. Matrix mat=ac.getMatrix();
  131. mat=mat.pow_M(mat,L);
  132. ull res=0;
  133. for(int i=0;i<mat.n;i++)res+=mat.mat[0][i];
  134. res--;
  135. ull ans=0;
  136. Matrix all=Matrix(2);all.mat[0][0]=26;all.mat[0][1]=all.mat[1][1]=1;
  137. /*
  138. 26 1 × Sn -> Sn+1
  139. 0 1 × 26 -> 26
  140. */
  141. all=all.pow_M(all,L);
  142. ans=all.mat[0][1]*26;
  143. cout<<ans-res<<endl;
  144. }
  145. return 0;
  146. }

G.POJ - 1625 Censored!

题意

n,m,p;n代表总共有n个字母,m代表字符串的长度为m,p代表病毒字符串的个数;题目让你求的是不包含病毒的字符串长度为m的个数为多少。

题解

较麻烦的题,但是不要被代码长度吓到了

先讲一讲思路,给出了可用字母和不可用单词,

那么首先想到将不可用单词建立AC自动机。

那么什么情况下会用到禁止使用的单词呢?

我们将Trie树中间被标记的点叫做危险结点。

在AC自动机中不断遍历,如果遇到危险结点,就是不合法的

考虑如何遍历,这里需要用到AC自动机建立失败指针的一个技巧

若当前结点无儿子结点,把失败指针处的儿子拿过来。

如果当前结点有儿子结点,把失败指针处的危险标记拿过来,详细见代码。

这样就方便了后面找后继结点。

不过没有取模操作,需要大数。

大数不可以矩阵快速幂吗?内存不够……

使用dp递推,递推公式也比较简单。

  1. #include<algorithm>
  2. #include <iostream>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <math.h>
  7. #include <time.h>
  8. #include <vector>
  9. #include <bitset>
  10. #include <queue>
  11. #include <map>
  12. #include <set>
  13. using namespace std;
  14. typedef long long ll;
  15. #define pp pair<int,int>
  16. const ll mod=998244353;
  17. const int maxn=1e5+50;
  18. const ll inf=0x3f3f3f3f3f3f3f3fLL;
  19. ll gcd(ll a,ll b){while(b){ll t=a%b;a=b;b=t;}return a;}
  20. ll lcm(ll a,ll b){return a*b/__gcd(a,b);}
  21. map<char,int>mp;
  22. int N,M,P;
  23. struct Matrix{
  24. int mat[110][110];
  25. int n;
  26. Matrix(){}
  27. Matrix(int _n){
  28. n=_n;
  29. for(int i=0;i<n;i++)for(int j=0;j<n;j++)mat[i][j]=0;
  30. }
  31. };
  32. struct Trie{
  33. int next[110][256],fail[110];bool end[110];
  34. int L,root;
  35. int newnode(){
  36. for(int i=0;i<256;i++){
  37. next[L][i]=-1;
  38. }
  39. end[L++]=false;
  40. return L-1;
  41. }
  42. void init(){
  43. L=0;
  44. root=newnode();
  45. }
  46. void insert(char buf[]){
  47. int len=strlen(buf);
  48. int now=root;
  49. for(int i=0;i<len;i++){
  50. if(next[now][mp[buf[i]]]==-1)
  51. next[now][mp[buf[i]]]=newnode();
  52. now=next[now][mp[buf[i]]];
  53. }
  54. end[now]=true;
  55. }
  56. void build(){
  57. queue<int>Q;
  58. fail[root]=root;
  59. for(int i=0;i<256;i++)
  60. if(next[root][i]==-1)next[root][i]=root;
  61. else{
  62. fail[next[root][i]]=root;
  63. Q.push(next[root][i]);
  64. }
  65. while(!Q.empty()){
  66. int now=Q.front();
  67. Q.pop();
  68. if(end[fail[now]]==true)end[now]=true;
  69. for(int i=0;i<256;i++)
  70. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  71. else{
  72. fail[next[now][i]]=next[fail[now]][i];
  73. Q.push(next[now][i]);
  74. }
  75. }
  76. }
  77. Matrix getMatrix(){
  78. Matrix res=Matrix(L);
  79. for(int i=0;i<L;i++)
  80. for(int j=0;j<N;j++)
  81. if(end[next[i][j]]==false)res.mat[i][next[i][j]]++;
  82. return res;
  83. }
  84. };
  85. struct BigInt{
  86. const static int mod=10000;
  87. const static int DLEN=4;
  88. int a[600],len;
  89. BigInt(){
  90. memset(a,0,sizeof(a));
  91. len=1;
  92. }
  93. BigInt(int v){
  94. memset(a,0,sizeof(a));
  95. len=0;
  96. do{
  97. a[len++]=v%mod;
  98. v/=mod;
  99. }while(v);
  100. }
  101. BigInt(const char s[]){
  102. memset(a,0,sizeof(a));
  103. int L=strlen(s);
  104. len=L/DLEN;
  105. if(L%DLEN)len++;
  106. int index=0;
  107. for(int i=L-1;i>=0;i-=DLEN){
  108. int t=0;
  109. int k=i-DLEN+1;
  110. if(k<0)k=0;
  111. for(int j=k;j<=i;j++)
  112. t=t*10+s[j]-'0';
  113. a[index]=t;
  114. }
  115. }
  116. BigInt operator +(const BigInt &b)const{
  117. BigInt res;
  118. res.len=max(len,b.len);
  119. for(int i=0;i<res.len;i++){
  120. res.a[i]+=((i<len)?a[i]:0)+((i<b.len)?b.a[i]:0);
  121. res.a[i+1]+=res.a[i]/mod;
  122. res.a[i]%=mod;
  123. }
  124. if(res.a[res.len]>0)res.len++;
  125. return res;
  126. }
  127. BigInt operator *(const BigInt &b)const
  128. {
  129. BigInt res;
  130. for(int i = 0; i < len;i++)
  131. {
  132. int up = 0;
  133. for(int j = 0;j < b.len;j++)
  134. {
  135. int temp = a[i]*b.a[j] + res.a[i+j] + up;
  136. res.a[i+j] = temp%mod;
  137. up = temp/mod;
  138. }
  139. if(up != 0)
  140. res.a[i + b.len] = up;
  141. }
  142. res.len = len + b.len;
  143. while(res.a[res.len - 1] == 0 &&res.len > 1)res.len--;
  144. return res;
  145. }
  146. void output()
  147. {
  148. printf("%d",a[len-1]);
  149. for(int i = len-2;i >=0 ;i--)
  150. printf("%04d",a[i]);
  151. printf("\n");
  152. }
  153. };
  154. char buf[1010];
  155. BigInt dp[2][110];
  156. Trie ac;
  157. int main()
  158. {
  159. while(scanf("%d%d%d",&N,&M,&P)==3){
  160. gets(buf);
  161. gets(buf);
  162. mp.clear();
  163. int len=strlen(buf);
  164. for(int i=0;i<len;i++)mp[buf[i]]=i;
  165. ac.init();
  166. for(int i=0;i<P;i++){
  167. gets(buf);
  168. ac.insert(buf);
  169. }
  170. ac.build();
  171. Matrix a=ac.getMatrix();
  172. int now=0;
  173. dp[now][0]=1;
  174. for(int i=1;i<a.n;i++)dp[now][i]=0;
  175. for(int i=0;i<M;i++){
  176. now^=1;
  177. for(int j=0;j<a.n;j++)dp[now][j]=0;
  178. for(int j=0;j<a.n;j++){
  179. for(int k=0;k<a.n;k++){
  180. if(a.mat[j][k]>0){
  181. dp[now][k]=dp[now][k]+dp[now^1][j]*a.mat[j][k];
  182. }
  183. }
  184. }
  185. }
  186. BigInt ans=0;
  187. for(int i=0;i<a.n;i++)ans=ans+dp[now][i];
  188. ans.output();
  189. }
  190. return 0;
  191. }

H.HDU - 2825 Wireless Password

题意

给你一堆(不超过10个)长度不超过10的单词,然后问你长度为N句子并且包含K个不同单词的个数有多少个;

题解

先利用ac自动机求出各种状态,(总共不超过10*10)个,然后dp枚举长度,状态,和已有个数,已有个数可以用状态压缩O(1)求出来

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=20090717;
  6. const int maxn=(1<<10)+5;
  7. const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  9. int lcm(int a,int b){return a*b/gcd(a,b);}
  10. int dp[110][110][maxn];
  11. struct Trie{
  12. int next[110][26],fail[110],end[110];
  13. int root,L;
  14. int newnode(){
  15. for(int i=0;i<26;i++)
  16. next[L][i]=-1;
  17. end[L++]=0;
  18. return L-1;
  19. }
  20. void init(){
  21. L=0;
  22. root=newnode();
  23. }
  24. void insert(char buf[],int id){
  25. int len=strlen(buf);
  26. int now=root;
  27. for(int i=0;i<len;i++){
  28. if(next[now][buf[i]-'a']==-1)
  29. next[now][buf[i]-'a']=newnode();
  30. now=next[now][buf[i]-'a'];
  31. }
  32. end[now]|=(1<<id);
  33. }
  34. void build(){
  35. queue<int>Q;
  36. fail[root]=root;
  37. for(int i=0;i<26;i++)
  38. if(next[root][i]==-1)
  39. next[root][i]=root;
  40. else{
  41. fail[next[root][i]]=root;
  42. Q.push(next[root][i]);
  43. }
  44. while(!Q.empty()){
  45. int now=Q.front();
  46. Q.pop();
  47. end[now]|=end[fail[now]];
  48. for(int i=0;i<26;i++)
  49. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  50. else{
  51. fail[next[now][i]]=next[fail[now]][i];
  52. Q.push(next[now][i]);
  53. }
  54. }
  55. }
  56. int query(char buf[]){
  57. int len=strlen(buf);
  58. int now=root;
  59. int res=0;
  60. for(int i=0;i<len;i++){
  61. now=next[now][buf[i]-'a'];
  62. int temp=now;
  63. while(temp!=root){
  64. res+=end[temp];
  65. end[temp]=0;
  66. temp=fail[temp];
  67. }
  68. }
  69. return res;
  70. }
  71. };
  72. Trie ac;
  73. int num[maxn];
  74. int slove(int n,int m,int k){
  75. memset(dp,0,sizeof(dp));
  76. dp[0][0][0]=1;
  77. for(int i=0;i<n;i++){//长度
  78. for(int j=0;j<ac.L;j++){//状态
  79. for(int l=0;l<(1<<m);l++){//个数
  80. if(dp[i][j][l]){
  81. for(int p=0;p<26;p++){
  82. int nowj=ac.next[j][p];
  83. int nowl=l|(ac.end[nowj]);
  84. dp[i+1][nowj][nowl]=(dp[i+1][nowj][nowl]+dp[i][j][l])%mod;
  85. }
  86. }
  87. }
  88. }
  89. }
  90. ll sum=0;
  91. for(int i=0;i<ac.L;i++){
  92. for(int j=0;j<(1<<m);j++){
  93. if(num[j]>=k){
  94. sum=(sum+dp[n][i][j])%mod;
  95. }
  96. }
  97. }
  98. return sum;
  99. }
  100. int n,m,k;
  101. char buf[150];
  102. int main()
  103. {
  104. std::ios::sync_with_stdio(false);
  105. std::cin.tie(0);
  106. std::cout.tie(0);
  107. memset(num,0,sizeof(num));
  108. for(int i=0;i<maxn;i++){
  109. for(int j=0;j<=10;j++){
  110. if(i&(1<<j))num[i]++;
  111. }
  112. }
  113. while(cin>>n>>m>>k){
  114. if(n==0&&m==0&&k==0)break;
  115. ac.init();
  116. for(int i=0;i<m;i++){
  117. cin>>buf;
  118. ac.insert(buf,i);
  119. }
  120. ac.build();
  121. cout<<slove(n,m,k)<<endl;
  122. }
  123. return 0;
  124. }

I.HDU - 2296 Ring

题意

  1. 给定m个不同的单词,单词由小写字母组成,每个单词都有自身的价值。现在你要设计一个字符串,字符串的长度不能超过n。如果字符串里面包含了某个单词,就可以获得相应的价值,价值可以重复计算。单词可以相互重叠。

题解

模板AC自动机,不过加了个字符串储存 不过不知道这是不是就是传说中的trie图

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=20090717;
  6. const int maxn=(1<<10)+5;
  7. //const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. const int inf=0x3f3f3f3f;
  9. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  10. int lcm(int a,int b){return a*b/gcd(a,b);}
  11. int dp[55][1100];
  12. string ss[55][1100];
  13. int money[150];
  14. int cmp(string a,string b){
  15. if(a.length()==b.length())return a<b;
  16. else return a.length()<b.length();
  17. }
  18. int mycmp(string a,string b){
  19. if(a.length()==b.length())return a<b;
  20. else return a.length()<b.length();
  21. }
  22. struct Trie{
  23. int next[1100][26],fail[1010],end[1010];
  24. int root,L;
  25. int newnode(){
  26. for(int i=0;i<26;i++)
  27. next[L][i]=-1;
  28. end[L++]=0;
  29. return L-1;
  30. }
  31. void init(){
  32. L=0;
  33. root=newnode();
  34. }
  35. void insert(char buf[],int id,int money){
  36. int len=strlen(buf);
  37. int now=root;
  38. for(int i=0;i<len;i++){
  39. if(next[now][buf[i]-'a']==-1)
  40. next[now][buf[i]-'a']=newnode();
  41. now=next[now][buf[i]-'a'];
  42. }
  43. end[now]=money;
  44. }
  45. void build(){
  46. queue<int>Q;
  47. fail[root]=root;
  48. for(int i=0;i<26;i++)
  49. if(next[root][i]==-1)
  50. next[root][i]=root;
  51. else{
  52. fail[next[root][i]]=root;
  53. Q.push(next[root][i]);
  54. }
  55. while(!Q.empty()){
  56. int now=Q.front();
  57. Q.pop();
  58. end[now]+=end[fail[now]];
  59. for(int i=0;i<26;i++)
  60. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  61. else{
  62. fail[next[now][i]]=next[fail[now]][i];
  63. Q.push(next[now][i]);
  64. }
  65. }
  66. }
  67. int query(char buf[]){
  68. int len=strlen(buf);
  69. int now=root;
  70. int res=0;
  71. for(int i=0;i<len;i++){
  72. now=next[now][buf[i]-'a'];
  73. int temp=now;
  74. while(temp!=root){
  75. res+=end[temp];
  76. end[temp]=0;
  77. temp=fail[temp];
  78. }
  79. }
  80. return res;
  81. }
  82. string slove(int n,int m){
  83. for(int i=0;i<=n;i++)for(int j=0;j<L;j++)ss[i][j]="";
  84. dp[0][0]=0;
  85. int ma=0;
  86. for(int i=0;i<n;i++){
  87. for(int j=0;j<L;j++){
  88. if(dp[i][j]!=-inf){
  89. for(int k=0;k<26;k++){
  90. if(next[j][k]!=-1){
  91. int nex=next[j][k];
  92. string nowstr=ss[i][j]+char(k+'a');
  93. int nowmoney=dp[i][j]+end[nex];
  94. if(nowmoney>dp[i+1][nex]||(nowmoney==dp[i+1][nex]&&mycmp(nowstr,ss[i+1][nex]))){
  95. dp[i+1][nex]=nowmoney;
  96. ss[i+1][nex]=nowstr;
  97. if(dp[i+1][nex]>ma){
  98. ma=dp[i+1][nex];
  99. }
  100. }
  101. }
  102. }
  103. }
  104. }
  105. }
  106. vector<string>ve;
  107. for(int i=0;i<=n;i++){
  108. for(int j=0;j<L;j++){
  109. if(dp[i][j]==ma){
  110. ve.push_back(ss[i][j]);
  111. }
  112. }
  113. }
  114. sort(ve.begin(),ve.end(),cmp);
  115. return ve[0];
  116. }
  117. };
  118. Trie ac;
  119. int n,m,k;
  120. char buf[150][150];
  121. int main()
  122. {
  123. std::ios::sync_with_stdio(false);
  124. std::cin.tie(0);
  125. std::cout.tie(0);
  126. int t;
  127. cin>>t;
  128. while(t--){
  129. cin>>n>>m;
  130. ac.init();
  131. for(int i=0;i<m;i++)cin>>buf[i];
  132. for(int i=0;i<m;i++)cin>>money[i],ac.insert(buf[i],i,money[i]);
  133. memset(dp,-inf,sizeof(dp));
  134. ac.build();
  135. cout<<ac.slove(n,m)<<endl;
  136. }
  137. return 0;
  138. }

J.HDU - 2457 DNA repair

题意

给你N个病毒串,然后给你一个长度为N的基因串,让你替换成没有病毒的基因串,求最小的替换次数(每次只能替换一个字符)

题解

DP I ,J I: 当前串的长度,J,结尾的位置

直接DP扫过去,用AC自动机构造出病毒的节点,然后开始构造,如果新的点和原来的串相同就

\[DP[i][j]=min(DP[i-1][j],dp[i][j])
\]

否则

\[DP[i][j]=min(dp[i-1][j]+1,dp[i][j])
\]

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=20090717;
  6. const int maxn=(1<<10)+5;
  7. //const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. const int inf=0x3f3f3f3f;
  9. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  10. int lcm(int a,int b){return a*b/gcd(a,b);}
  11. int dp[1050][1050];
  12. char buf[1500];
  13. struct Trie{
  14. int next[1100][4],fail[1010];
  15. bool end[1010];
  16. char s['Z'+1];
  17. int root,L;
  18. int newnode(){
  19. for(int i=0;i<4;i++)
  20. next[L][i]=-1;
  21. end[L++]=false;
  22. return L-1;
  23. }
  24. void init(){
  25. L=0;
  26. s['A']=0;s['G']=1;s['T']=2;s['C']=3;
  27. root=newnode();
  28. }
  29. void insert(char buf[]){
  30. int len=strlen(buf);
  31. int now=root;
  32. for(int i=0;i<len;i++){
  33. if(next[now][s[buf[i]]]==-1)
  34. next[now][s[buf[i]]]=newnode();
  35. now=next[now][s[buf[i]]];
  36. }
  37. end[now]=true;
  38. }
  39. void build(){
  40. queue<int>Q;
  41. fail[root]=root;
  42. for(int i=0;i<4;i++)
  43. if(next[root][i]==-1)
  44. next[root][i]=root;
  45. else{
  46. fail[next[root][i]]=root;
  47. Q.push(next[root][i]);
  48. }
  49. while(!Q.empty()){
  50. int now=Q.front();
  51. Q.pop();
  52. if(end[fail[now]])end[now]=true;
  53. for(int i=0;i<4;i++)
  54. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  55. else{
  56. fail[next[now][i]]=next[fail[now]][i];
  57. Q.push(next[now][i]);
  58. }
  59. }
  60. }
  61. void slove(int n){
  62. memset(dp,inf,sizeof(dp));
  63. dp[0][0]=0;
  64. for(int i=0;i<n;i++){
  65. for(int j=0;j<L;j++){
  66. if(dp[i][j]!=inf){
  67. for(int k=0;k<4;k++){
  68. int nex=next[j][k];
  69. if(end[nex])continue;
  70. if(s[buf[i]]==k){
  71. dp[i+1][nex]=min(dp[i+1][nex],dp[i][j]);
  72. }
  73. else{
  74. dp[i+1][nex]=min(dp[i+1][nex],dp[i][j]+1);
  75. }
  76. }
  77. }
  78. }
  79. }
  80. int mi=inf;
  81. for(int i=0;i<L;i++){
  82. if(dp[n][i]<mi)mi=dp[n][i];
  83. }
  84. if(mi==inf)cout<<-1<<endl;
  85. else cout<<mi<<endl;
  86. }
  87. };
  88. Trie ac;
  89. int n;
  90. int main()
  91. {
  92. std::ios::sync_with_stdio(false);
  93. std::cin.tie(0);
  94. std::cout.tie(0);
  95. int t=0;
  96. while(cin>>n){
  97. if(n==0)break;
  98. ac.init();
  99. for(int i=0;i<n;i++){
  100. cin>>buf;
  101. ac.insert(buf);
  102. }
  103. ac.build();
  104. cin>>buf;
  105. cout<<"Case "<<++t<<": ";
  106. int len=strlen(buf);
  107. ac.slove(len);
  108. }
  109. return 0;
  110. }

K.ZOJ - 3228 Searching the String

题意

先给你一个字符串,然后给你若干个子串,0代表能够重叠,1代表不能重叠,求出如今母串的次数。

题解

多一个推断的是假设这个子串与上次出现的次数大于子串长度的话。就代表这次不是重叠的了

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=20090717;
  6. const int maxn=600010;
  7. //const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. const int inf=0x3f3f3f3f;
  9. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  10. int lcm(int a,int b){return a*b/gcd(a,b);}
  11. char buf[100010];
  12. int ans[maxn][2];
  13. struct Trie{
  14. int next[maxn][26],fail[maxn],deep[maxn],last[maxn];
  15. int end[100010];
  16. int root,L;
  17. int newnode(){
  18. for(int i=0;i<26;i++)
  19. next[L][i]=-1;
  20. deep[L++]=0;
  21. return L-1;
  22. }
  23. void init(){
  24. L=0;
  25. root=newnode();
  26. }
  27. void insert(char buf[],int id){
  28. int len=strlen(buf);
  29. int now=root;
  30. for(int i=0;i<len;i++){
  31. if(next[now][buf[i]-'a']==-1)
  32. next[now][buf[i]-'a']=newnode();
  33. deep[next[now][buf[i]-'a']]=deep[now]+1;
  34. now=next[now][buf[i]-'a'];
  35. }
  36. end[id]=now;
  37. }
  38. void build(){
  39. queue<int>Q;
  40. fail[root]=root;
  41. for(int i=0;i<26;i++)
  42. if(next[root][i]==-1)
  43. next[root][i]=root;
  44. else{
  45. fail[next[root][i]]=root;
  46. Q.push(next[root][i]);
  47. }
  48. while(!Q.empty()){
  49. int now=Q.front();
  50. Q.pop();
  51. for(int i=0;i<26;i++)
  52. if(next[now][i]==-1)
  53. next[now][i]=next[fail[now]][i];
  54. else{
  55. fail[next[now][i]]=next[fail[now]][i];
  56. Q.push(next[now][i]);
  57. }
  58. }
  59. }
  60. void slove(char buf[]){
  61. for(int i=0;i<L;i++){
  62. ans[i][0]=ans[i][1]=0;
  63. last[i]=-1;
  64. }
  65. int len=strlen(buf);
  66. int now=root;
  67. for(int i=0;i<len;i++){
  68. now=next[now][buf[i]-'a'];
  69. int temp=now;
  70. while(temp!=root){
  71. ans[temp][0]++;
  72. if(i-last[temp]>=deep[temp]){
  73. ans[temp][1]++;
  74. last[temp]=i;
  75. }
  76. temp=fail[temp];
  77. }
  78. }
  79. }
  80. };
  81. Trie ac;
  82. int n;
  83. char s[10];
  84. int flag[100010];
  85. void neww(){
  86. int n, cas = 1;
  87. while (scanf("%s", buf) != EOF) {
  88. scanf("%d", &n);
  89. ac.init();
  90. for (int i = 0; i < n; i++) {
  91. scanf("%d%s", &flag[i], s);
  92. ac.insert(s, i);
  93. }
  94. ac.build();
  95. ac.slove(buf);
  96. printf("Case %d\n", cas++);
  97. for (int i = 0; i < n; i++)
  98. printf("%d\n", ans[ac.end[i]][flag[i]]);
  99. printf("\n");
  100. }
  101. }
  102. int main()
  103. {
  104. neww();
  105. return 0;
  106. }

L.HDU - 3341 Lost's revenge

题意

给出N个模式串(len<=10)和一个目标长串(len<=40)求将目标串重新排列后所能包含的模式串个数

题解

首先重排列,所以目标串中的ACGT个数不能变化

刚开始想开个五维数组来存取状态,但是空间会爆炸;

所以搜了一下发现可以用模拟进制来把 ACGT的个数用一个数字表达

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=998244353;
  6. const int maxn=15000;
  7. //const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. const int inf=0x3f3f3f3f;
  9. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  10. int lcm(int a,int b){return a*b/gcd(a,b);}
  11. int dp[505][maxn];
  12. struct Trie{
  13. int next[505][4],fail[505],end[505];
  14. int ok['Z'];
  15. int root,L;
  16. int newnode(){
  17. for(int i=0;i<4;i++)next[L][i]=-1;
  18. end[L++]=0;
  19. return L-1;
  20. }
  21. void init(){
  22. L=0;ok['A']=0;ok['G']=1;ok['C']=2;ok['T']=3;
  23. root=newnode();
  24. }
  25. void insert(char buf[]){
  26. int len=strlen(buf);
  27. int now=root;
  28. for(int i=0;i<len;i++){
  29. int ch=ok[buf[i]];
  30. if(next[now][ch]==-1){
  31. next[now][ch]=newnode();
  32. }
  33. now=next[now][ch];
  34. }end[now]++;
  35. }
  36. void build(){
  37. queue<int>Q;
  38. fail[root]=root;
  39. for(int i=0;i<4;i++)
  40. if(next[root][i]==-1)next[root][i]=root;
  41. else{
  42. fail[next[root][i]]=root;
  43. Q.push(next[root][i]);
  44. }
  45. while(!Q.empty()){
  46. int now=Q.front();
  47. Q.pop();
  48. end[now]+=end[fail[now]];
  49. for(int i=0;i<4;i++)
  50. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  51. else{
  52. fail[next[now][i]]=next[fail[now]][i];
  53. Q.push(next[now][i]);
  54. }
  55. }
  56. }
  57. int slove(char buf[]){
  58. int len=strlen(buf);
  59. int num[4]={0,0,0,0};
  60. memset(dp,-inf,sizeof(dp));
  61. for(int i=0;i<len;i++){
  62. num[ok[buf[i]]]++;
  63. }
  64. dp[0][0]=0;
  65. int bit[4];
  66. bit[0]=1;
  67. bit[1]=(num[0]+1);
  68. bit[2]=(num[1]+1)*(num[0]+1);
  69. bit[3]=(num[2]+1)*(num[1]+1)*(num[0]+1);
  70. for(int i=0;i<=num[0];i++)
  71. for(int j=0;j<=num[1];j++)
  72. for(int k=0;k<=num[2];k++)
  73. for(int l=0;l<=num[3];l++){
  74. int nowsta=i*bit[0]+j*bit[1]+k*bit[2]+l*bit[3];
  75. for(int p=0;p<L;p++){
  76. if(dp[p][nowsta]!=inf){
  77. for(int kk=0;kk<4;kk++){
  78. if(i==num[0]&&kk==0)continue;
  79. else if(j==num[1]&&kk==1)continue;
  80. else if(k==num[2]&&kk==2)continue;
  81. else if(l==num[3]&&kk==3)continue;
  82. int nexsta=nowsta+bit[kk];
  83. int nex=next[p][kk];
  84. dp[nex][nexsta]=max(dp[nex][nexsta],dp[p][nowsta]+end[nex]);
  85. }
  86. }
  87. }
  88. }
  89. int ans=num[0]*bit[0]+num[1]*bit[1]+num[2]*bit[2]+num[3]*bit[3];
  90. int ma=0;
  91. for(int i=0;i<L;i++)ma=max(dp[i][ans],ma);
  92. return ma;
  93. }
  94. };
  95. char buf[45];
  96. Trie ac;
  97. int main()
  98. {
  99. std::ios::sync_with_stdio(false);
  100. std::cin.tie(0);
  101. std::cout.tie(0);
  102. int n;
  103. int cnt=1;
  104. while(cin>>n){
  105. if(n==0)break;
  106. ac.init();
  107. for(int i=0;i<n;i++){
  108. cin>>buf;
  109. ac.insert(buf);
  110. }
  111. ac.build();
  112. cin>>buf;
  113. cout<<"Case "<<cnt++<<": ";
  114. cout<<ac.slove(buf)<<endl;
  115. }
  116. return 0;
  117. }

M.HDU - 3247 Resource Archiver

题意

给定n个文本串,m个病毒串,文本串重叠部分可以合并,但合并后不能含有病毒串,问所有文本串合并后最短多长。

思路

把病毒和文本出串塞入AC自动机中,然后把每个文本串的结点 和另一个文本串的结点的最短路求出来,在用状态DP求出每个点到另一个点之间的距离

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=998244353;
  6. const int maxn=60000+50;
  7. //const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. const int inf=0x3f3f3f3f;
  9. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  10. int lcm(int a,int b){return a*b/gcd(a,b);}
  11. struct Trie{
  12. int next[maxn][2],fail[maxn];
  13. int end[maxn],dp[1025][11];
  14. int mat[11][11];
  15. int pos[11];
  16. int dis[maxn];
  17. int root,L;
  18. int cnt;
  19. int newnode(){
  20. for(int i=0;i<2;i++)next[L][i]=-1;
  21. end[L++]=0;
  22. return L-1;
  23. }
  24. void init(){
  25. L=0;
  26. root=newnode();
  27. }
  28. void insert(char buf[],int id){
  29. int len=strlen(buf);
  30. int now=root;
  31. for(int i=0;i<len;i++){
  32. int ch=buf[i]-'0';
  33. if(next[now][ch]==-1){
  34. next[now][ch]=newnode();
  35. }
  36. now=next[now][ch];
  37. }
  38. if(id==-1||end[now]==-1)end[now]=-1;
  39. else end[now]|=(1<<id);
  40. }
  41. void build(){
  42. queue<int>Q;
  43. fail[root]=root;
  44. for(int i=0;i<2;i++)
  45. if(next[root][i]==-1)next[root][i]=root;
  46. else{
  47. fail[next[root][i]]=root;
  48. Q.push(next[root][i]);
  49. }
  50. while(!Q.empty()){
  51. int now=Q.front();
  52. Q.pop();
  53. if(end[fail[now]]==-1)end[now]=-1;
  54. else end[now]|=end[fail[now]];
  55. for(int i=0;i<2;i++)
  56. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  57. else{
  58. fail[next[now][i]]=next[fail[now]][i];
  59. Q.push(next[now][i]);
  60. }
  61. }
  62. }
  63. void bfs(int id){
  64. queue<int>Q;
  65. Q.push(pos[id]);
  66. memset(dis,-1,sizeof(dis));
  67. dis[pos[id]]=0;
  68. while(!Q.empty()){
  69. int now=Q.front();
  70. Q.pop();
  71. for(int i=0;i<2;i++){
  72. if(end[next[now][i]]>=0&&dis[next[now][i]]<0){
  73. dis[next[now][i]]=dis[now]+1;
  74. Q.push(next[now][i]);
  75. }
  76. }
  77. }
  78. for(int i=0;i<cnt;i++)
  79. mat[id][i]=dis[pos[i]];
  80. return;
  81. }
  82. void slove(int n){
  83. pos[0]=0;
  84. cnt=1;
  85. for(int i=0;i<L;i++)
  86. if(end[i]>0)pos[cnt++]=i;
  87. for(int i=0;i<cnt;i++){
  88. bfs(i);
  89. }
  90. for(int i=0;i<(1<<n);i++)
  91. for(int j=0;j<cnt;j++)
  92. dp[i][j]=inf;
  93. dp[0][0]=0;
  94. for(int i=0;i<(1<<n);i++)
  95. for(int j=0;j<cnt;j++)
  96. if(dp[i][j]!=inf){
  97. for(int k=0;k<cnt;k++){
  98. if(mat[j][k]<0)continue;
  99. dp[i|end[pos[k]]][k]=min(dp[i|end[pos[k]]][k],dp[i][j]+mat[j][k]);
  100. }
  101. }
  102. int ans=inf;
  103. for(int i=0;i<cnt;i++)ans=min(ans,dp[(1<<n)-1][i]);
  104. cout<<ans<<endl;
  105. return;
  106. }
  107. };
  108. char buf[55000];
  109. Trie ac;
  110. int main()
  111. {
  112. std::ios::sync_with_stdio(false);
  113. std::cin.tie(0);
  114. std::cout.tie(0);
  115. int n,m;
  116. while(cin>>n>>m){
  117. if(n==0&&m==0)break;
  118. ac.init();
  119. for(int i=0;i<n;i++){
  120. cin>>buf;
  121. ac.insert(buf,i);
  122. }
  123. for(int i=0;i<m;i++){
  124. cin>>buf;
  125. ac.insert(buf,-1);
  126. }
  127. ac.build();
  128. ac.slove(n);
  129. }
  130. return 0;
  131. }

N.ZOJ - 3494 BCD Code

题意

跟数位DP的一题重复了 这里再做一次

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=1e9+9;
  6. const int maxn=2200;
  7. //const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. const int inf=0x3f3f3f3f;
  9. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  10. int lcm(int a,int b){return a*b/gcd(a,b);}
  11. ll dp[300][maxn];
  12. int num[300];
  13. char nu[10][5]={"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001"};
  14. struct Trie{
  15. int next[maxn][2],fail[maxn];
  16. bool end[maxn];
  17. int root,L;
  18. int newnode(){
  19. for(int i=0;i<2;i++)next[L][i]=-1;
  20. end[L++]=false;
  21. return L-1;
  22. }
  23. void init(){
  24. L=0;
  25. root=newnode();
  26. }
  27. void insert(char buf[]){
  28. int len=strlen(buf);
  29. int now=root;
  30. for(int i=0;i<len;i++){
  31. int ch=buf[i]-'0';
  32. if(next[now][ch]==-1){
  33. next[now][ch]=newnode();
  34. }
  35. now=next[now][ch];
  36. }
  37. end[now]=true;
  38. }
  39. void build(){
  40. queue<int>Q;
  41. fail[root]=root;
  42. for(int i=0;i<2;i++)
  43. if(next[root][i]==-1)next[root][i]=root;
  44. else{
  45. fail[next[root][i]]=root;
  46. Q.push(next[root][i]);
  47. }
  48. while(!Q.empty()){
  49. int now=Q.front();
  50. Q.pop();
  51. if(end[fail[now]])end[now]=true;
  52. for(int i=0;i<2;i++)
  53. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  54. else{
  55. fail[next[now][i]]=next[fail[now]][i];
  56. Q.push(next[now][i]);
  57. }
  58. }
  59. }
  60. ll dfs(int pos,int in,bool zero,bool limit){
  61. if(pos==-1)return 1;
  62. if(dp[pos][in]!=-1&&!limit)return dp[pos][in];
  63. int up=limit?num[pos]:9;
  64. ll ans=0;
  65. for(int i=0;i<=up;i++){
  66. int now=in;
  67. if(zero&&i==0)ans=(ans+dfs(pos-1,in,zero&&i==0,limit&&i==up))%mod;
  68. else{
  69. bool flag=0;
  70. for(int j=0;j<4;j++){
  71. int ne=nu[i][j]-'0';
  72. now=next[now][ne];
  73. if(end[now]){
  74. flag=1;break;
  75. }
  76. }
  77. if(!flag)ans=(ans+dfs(pos-1,now,zero&&i==0,limit&&i==up))%mod;
  78. }
  79. }
  80. if(!limit&&!zero)dp[pos][in]=ans;
  81. return ans;
  82. }
  83. ll Ac(char buf[]){
  84. int len=strlen(buf);
  85. for(int i=0;i<len;i++){
  86. num[i]=int(buf[len-1-i]-'0');
  87. }
  88. return dfs(len-1,0,true,true);
  89. }
  90. };
  91. char buf[22];
  92. Trie ac;
  93. char a[250],b[250];
  94. int main()
  95. {
  96. std::ios::sync_with_stdio(false);
  97. std::cin.tie(0);
  98. std::cout.tie(0);
  99. int t;
  100. cin>>t;
  101. while(t--){
  102. ac.init();
  103. int n;
  104. cin>>n;
  105. memset(dp,-1,sizeof(dp));
  106. for(int i=0;i<n;i++){
  107. cin>>buf;
  108. ac.insert(buf);
  109. }
  110. ac.build();
  111. cin>>a>>b;
  112. int len=strlen(a);
  113. while(a[len-1]=='0'){
  114. a[len-1]='9';
  115. len--;
  116. }
  117. a[len-1]--;
  118. cout<<((ac.Ac(b)-ac.Ac(a)+mod)%mod)<<endl;
  119. }
  120. return 0;
  121. }

O.HDU - 4758 Walk Through Squares

题意

给你一个n*m的网格,现在你要从(1,1)走到(n,m),每次只能向右走或者向下走,走完后会形成一个包含R,D的序列,这个序列必须要包含题目给出的两个字符串,问有多少种方案。

题解

由于要从(1,1)走到(n,m),所以这个形成的字符串包含R和D的数量是一定的。

现在考虑dp i j k sta,表示包含两种串的组合状态,走了i个D,走了j个R,走到了k这个AC自动机的节点的方案数,串1和串2的状态sta 然后dp一下就行了。复杂度为O(3×n×m×len(s1)×len(s2))

按理说应该是N个D M个R 可是这样我就错了,换一下才AC。。。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=1e9+7;
  6. const int maxn=1010;
  7. //const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. const int inf=0x3f3f3f3f;
  9. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  10. int lcm(int a,int b){return a*b/gcd(a,b);}
  11. int dp[110][110][210][4];
  12. struct Trie{
  13. int next[maxn][2],fail[maxn];
  14. int end[maxn];
  15. int ID['Z'+1];
  16. int root,L;
  17. int newnode(){
  18. for(int i=0;i<2;i++)next[L][i]=-1;
  19. end[L++]=false;
  20. return L-1;
  21. }
  22. void init(){
  23. L=0;
  24. ID['R']=0;ID['D']=1;
  25. root=newnode();
  26. }
  27. void insert(char buf[],int id){
  28. int len=strlen(buf);
  29. int now=root;
  30. for(int i=0;i<len;i++){
  31. int ch=ID[buf[i]];
  32. if(next[now][ch]==-1){
  33. next[now][ch]=newnode();
  34. }
  35. now=next[now][ch];
  36. }
  37. end[now]=(1<<id);
  38. }
  39. void build(){
  40. queue<int>Q;
  41. fail[root]=root;
  42. for(int i=0;i<2;i++)
  43. if(next[root][i]==-1)next[root][i]=root;
  44. else{
  45. fail[next[root][i]]=root;
  46. Q.push(next[root][i]);
  47. }
  48. while(!Q.empty()){
  49. int now=Q.front();
  50. Q.pop();
  51. end[now]|=end[fail[now]];
  52. for(int i=0;i<2;i++)
  53. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  54. else{
  55. fail[next[now][i]]=next[fail[now]][i];
  56. Q.push(next[now][i]);
  57. }
  58. }
  59. }
  60. void slove(int n,int m){
  61. memset(dp,0,sizeof(dp));
  62. dp[0][0][0][0]=1;
  63. for(int i=0;i<=n;i++)
  64. for(int j=0;j<=m;j++)
  65. for(int k=0;k<L;k++)
  66. for(int sta=0;sta<(1<<2);sta++){
  67. if(dp[i][j][k][sta]){
  68. for(int p=0;p<2;p++){
  69. if(p==0){//右
  70. dp[i+1][j][next[k][p]][sta|end[next[k][p]]]+=dp[i][j][k][sta];
  71. dp[i+1][j][next[k][p]][sta|end[next[k][p]]]%=mod;
  72. }
  73. else{
  74. dp[i][j+1][next[k][p]][sta|end[next[k][p]]]+=dp[i][j][k][sta];
  75. dp[i][j+1][next[k][p]][sta|end[next[k][p]]]%=mod;
  76. }
  77. }
  78. }
  79. }
  80. int ans=0;
  81. for(int i=0;i<L;i++){
  82. ans+=dp[n][m][i][3];
  83. ans%=mod;
  84. }
  85. cout<<ans<<endl;
  86. }
  87. };
  88. char buf[110];
  89. Trie ac;
  90. int main()
  91. {
  92. std::ios::sync_with_stdio(false);
  93. std::cin.tie(0);
  94. std::cout.tie(0);
  95. int t;
  96. cin>>t;
  97. while(t--){
  98. int n,m;
  99. cin>>n>>m;
  100. ac.init();
  101. for(int i=0;i<2;i++){
  102. cin>>buf;
  103. ac.insert(buf,i);
  104. }
  105. ac.build();
  106. ac.slove(n,m);
  107. }
  108. return 0;
  109. }

P.HDU - 4511 小明系列故事――女友的考验

题意中文题

题解

设dp i j 表示到第i个点,自动机状态到j的最小步数,注意的是因为我们是根据不合法的路径构造自动机的,所以我们是不能走到某一条路径的末尾的,根据这点来记录我们的终止条件

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=1e9+7;
  6. const int maxn=1010;
  7. //const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. const double inf=1e20;
  9. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  10. int lcm(int a,int b){return a*b/gcd(a,b);}
  11. pp p[101];
  12. double dp[55][1000];
  13. double dis(pp a,pp b){
  14. return sqrt((double)(1.0*a.first-b.first)*(double)(1.0*a.first-b.first)+(double)(1.0*a.second-b.second)*(double)(1.0*a.second-b.second));
  15. }
  16. int n;
  17. struct Trie{
  18. int next[maxn][55],fail[maxn];
  19. int end[maxn];
  20. int root,L;
  21. int newnode(){
  22. for(int i=1;i<=n;i++)next[L][i]=-1;
  23. end[L++]=0;
  24. return L-1;
  25. }
  26. void init(){
  27. L=0;
  28. root=newnode();
  29. }
  30. void insert(int buf[],int k){
  31. int now=root;
  32. for(int i=0;i<k;i++){
  33. if(next[now][buf[i]]==-1){
  34. next[now][buf[i]]=newnode();
  35. }
  36. now=next[now][buf[i]];
  37. }
  38. end[now]=1;
  39. }
  40. void build(){
  41. queue<int>Q;
  42. fail[root]=root;
  43. for(int i=1;i<=n;i++)
  44. if(next[root][i]==-1)next[root][i]=root;
  45. else{
  46. fail[next[root][i]]=root;
  47. Q.push(next[root][i]);
  48. }
  49. while(!Q.empty()){
  50. int now=Q.front();
  51. Q.pop();
  52. end[now]|=end[fail[now]];
  53. for(int i=1;i<=n;i++)
  54. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  55. else{
  56. fail[next[now][i]]=next[fail[now]][i];
  57. Q.push(next[now][i]);
  58. }
  59. }
  60. }
  61. void slove(){
  62. for(int i=1;i<=n;i++)for(int j=0;j<L;j++)dp[i][j]=inf;
  63. dp[1][next[root][1]]=0;
  64. for(int i=1;i<n;i++)
  65. for(int j=0;j<L;j++)
  66. if(dp[i][j]!=inf){
  67. for(int k=i+1;k<=n;k++){
  68. int now=next[j][k];
  69. if(end[now])continue;
  70. dp[k][now]=min(dp[k][now],dp[i][j]+dis(p[i],p[k]));
  71. }
  72. }
  73. double ans=inf;
  74. for(int i=0;i<L;i++)ans=min(ans,dp[n][i]);
  75. if(ans==inf)cout<<"Can not be reached!"<<endl;
  76. else printf("%.2f\n",ans);
  77. }
  78. };
  79. int buf[110];
  80. Trie ac;
  81. int main()
  82. {
  83. /*std::ios::sync_with_stdio(false);
  84. std::cin.tie(0);
  85. std::cout.tie(0);*/
  86. int m;
  87. while(cin>>n>>m){
  88. if(n==0&&m==0)break;
  89. for(int i=1;i<=n;i++)cin>>p[i].first>>p[i].second;
  90. ac.init();
  91. while(m--){
  92. int k;
  93. cin>>k;
  94. for(int i=0;i<k;i++)cin>>buf[i];
  95. ac.insert(buf,k);
  96. }
  97. ac.build();
  98. ac.slove();
  99. }
  100. return 0;
  101. }

Q.附加题1 牛客网暑期ACM多校训练营(第九场)- Typing practice

题意

有n(n<=4)个长度为len的字符串,以及一个长度为len2的操作串。每一次你将选取操作串中长度为i(0<=i<=len2)的前缀,问你最少在这个前缀后加多少个字符,使得新字符串的后缀中能够至少出现这n个字符串中的一个。

题解

因为题目中设计多个串的匹配一个长串的问题,我们可以考虑使用AC自动机进行处理。

再考虑题目中要求我们求出匹配串的后缀凑出模式串的最小长度,因此,我们可以将这样的一个问题转化成:在一个Trie图中,处于第i个结点的字符到达任意一个模式串终点j的最短路。

因此,我们只需要利用AC自动机,将利用失配指针的性质,先将整张Trie图建立出来,并将每一个结点到达任意终点的最短路用bfs求出即可。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define pp pair<int,int>
  5. const ll mod=1e9+7;
  6. const int maxn=450000;
  7. //const ll inf=0x3f3f3f3f3f3f3f3fLL;
  8. const int inf=0x3f3f3f3f;
  9. int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
  10. int lcm(int a,int b){return a*b/gcd(a,b);}
  11. struct Trie{
  12. int next[maxn][26],fail[maxn];
  13. bool end[maxn];
  14. int ans[maxn];
  15. vector<int>ve[maxn];
  16. set<int>st;
  17. int root,L;
  18. int newnode(){
  19. for(int i=0;i<26;i++)next[L][i]=-1;
  20. ans[L]=inf;
  21. end[L++]=false;
  22. return L-1;
  23. }
  24. void init(){
  25. L=0;
  26. st.clear();
  27. root=newnode();
  28. }
  29. void insert(char buf[]){
  30. int len=strlen(buf);
  31. int now=root;
  32. for(int i=0;i<len;i++){
  33. if(next[now][buf[i]-'a']==-1){
  34. next[now][buf[i]-'a']=newnode();
  35. }
  36. now=next[now][buf[i]-'a'];
  37. }
  38. end[now]=true;
  39. ans[now]=0;
  40. st.insert(now);
  41. }
  42. void build(){
  43. queue<int>Q;
  44. fail[root]=root;
  45. for(int i=0;i<26;i++)
  46. if(next[root][i]==-1)next[root][i]=root;
  47. else{
  48. fail[next[root][i]]=root;
  49. Q.push(next[root][i]);
  50. }
  51. while(!Q.empty()){
  52. int now=Q.front();
  53. Q.pop();
  54. if(end[fail[now]]){
  55. end[now]=true;
  56. ans[now]=0;
  57. st.insert(now);
  58. }
  59. for(int i=0;i<26;i++)
  60. if(next[now][i]==-1)next[now][i]=next[fail[now]][i];
  61. else{
  62. fail[next[now][i]]=next[fail[now]][i];
  63. Q.push(next[now][i]);
  64. }
  65. }
  66. }
  67. void slove(char buf[]){
  68. for(int i=0;i<L;i++){
  69. for(int j=0;j<26;j++){
  70. ve[next[i][j]].push_back(i);
  71. }
  72. }
  73. queue<int>q;
  74. for(auto i:st){
  75. q.push(i);
  76. }
  77. while(!q.empty()){
  78. int now=q.front();
  79. q.pop();
  80. for(int i=0;i<ve[now].size();i++){
  81. if(ans[ve[now][i]]>ans[now]+1){
  82. ans[ve[now][i]]=min(ans[ve[now][i]],ans[now]+1);
  83. q.push(ve[now][i]);
  84. }
  85. }
  86. }
  87. int len=strlen(buf);
  88. stack<int>ss;
  89. int now=root;
  90. cout<<ans[now]<<endl;
  91. for(int i=0;i<len;i++){
  92. if(buf[i]=='-'&&ss.empty()){
  93. now=root;
  94. }
  95. else if(buf[i]=='-'){
  96. now=ss.top();
  97. ss.pop();
  98. }
  99. else{
  100. ss.push(now);
  101. now=next[now][buf[i]-'a'];
  102. }
  103. cout<<ans[now]<<endl;
  104. }
  105. }
  106. };
  107. char buf[110000];
  108. Trie ac;
  109. int main()
  110. {
  111. std::ios::sync_with_stdio(false);
  112. std::cin.tie(0);
  113. std::cout.tie(0);
  114. int n;
  115. cin>>n;
  116. ac.init();
  117. for(int i=0;i<n;i++){
  118. cin>>buf;
  119. ac.insert(buf);
  120. }
  121. ac.build();
  122. cin>>buf;
  123. ac.slove(buf);
  124. return 0;
  125. }

「kuangbin带你飞」专题十七 AC自动机的更多相关文章

  1. 「kuangbin带你飞」专题十四 数论基础

    layout: post title: 「kuangbin带你飞」专题十四 数论基础 author: "luowentaoaa" catalog: true tags: mathj ...

  2. 「kuangbin带你飞」专题二十 斜率DP

    layout: post title: 「kuangbin带你飞」专题二十 斜率DP author: "luowentaoaa" catalog: true tags: mathj ...

  3. 「kuangbin带你飞」专题二十二 区间DP

    layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - ku ...

  4. 「kuangbin带你飞」专题十九 矩阵

    layout: post title: 「kuangbin带你飞」专题十九 矩阵 author: "luowentaoaa" catalog: true tags: mathjax ...

  5. 「kuangbin带你飞」专题十八 后缀数组

    layout: post title: 「kuangbin带你飞」专题十八 后缀数组 author: "luowentaoaa" catalog: true tags: - kua ...

  6. 「kuangbin带你飞」专题十二 基础DP

    layout: post title: 「kuangbin带你飞」专题十二 基础DP author: "luowentaoaa" catalog: true tags: mathj ...

  7. 「kuangbin带你飞」专题十五 数位DP

    传送门 A.CodeForces - 55D Beautiful numbers 题意 一个正整数是 漂亮数 ,当且仅当它能够被自身的各非零数字整除.我们不必与之争辩,只需计算给定范围中有多少个漂亮数 ...

  8. kuangbin带你飞dp专题-基础dp

    dp HDU - 1257 最少拦截系统 最长递增子序列 #include<iostream> using namespace std; const int maxn=1e7; int a ...

  9. kuangbin带你飞 生成树专题 : 次小生成树; 最小树形图;生成树计数

    第一个部分 前4题 次小生成树 算法:首先如果生成了最小生成树,那么这些树上的所有的边都进行标记.标记为树边. 接下来进行枚举,枚举任意一条不在MST上的边,如果加入这条边,那么肯定会在这棵树上形成一 ...

随机推荐

  1. .netCore 反射 :Could not load file or assembly 系统找不到指定文件

    “System.IO.FileNotFoundException:“Could not load file or assembly 'ClassLibrary2, Culture=neutral, P ...

  2. 【java并发编程实战】第一章笔记

    1.线程安全的定义 当多个线程访问某个类时,不管允许环境采用何种调度方式或者这些线程如何交替执行,这个类都能表现出正确的行为 如果一个类既不包含任何域,也不包含任何对其他类中域的引用.则它一定是无状态 ...

  3. Python全栈 MySQL 数据库 (表字段增、删、改、查、函数)

    ParisGabriel              每天坚持手写  一天一篇  决定坚持几年 为了梦想为了信仰    开局一张图         查询SQL变量 show variables 1.表字 ...

  4. Ubuntu下禁用笔记本自带键盘

    想要禁用笔记本自带键盘(Ubuntu)只要2条命令. 1. 打开终端,输入: xinput list ⎡ Virtual core pointer id=2 [master pointer (3)] ...

  5. css深入理解vertical-align

    第一讲:vertical-align家族基本认识 了解vertical-align支持的属性值以及组成 属性: 1.inherit 2.线类 baseline,top,middle,bottom 3. ...

  6. juqery基本选择器和层级选择器

    1.基本选择器,主要通过标签名称,样式,id等选择标签,如下代码是简单的使用 (1)根据标签或者样式筛选 <!DOCTYPE html> <html lang="en&qu ...

  7. HDU 4417 Super Mario ( 离线树状数组 )

    把数值和查询放在一起从小到大排序,纪录每个数值的位置,当遇到数值时就更新到树状数组中,遇到查询就直接查询该区间和. #include <cstdio> #include <cstri ...

  8. hdu 1564 Play a game (博弈)

    Play a game Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. vue的过渡效果

    https://segmentfault.com/a/1190000007738518

  10. lesson 5

    C#中的委托(delegate)与事件(event) 一.委托就是中间人的意思,c#中的委托允许将一个类中的方法传递给另一个能调用该方法的类的某个对象.程序员可以将A类的一个方法m(被包含在某个del ...