有了KMP和Trie的基础,就可以学习神奇的AC自动机了。AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配。
AC自动机 其实 就是创建了一个状态的转移图,思想很重要。
推荐的学习链接:
http://acm.uestc.edu.cn/bbs/read.php?tid=4294
http://blog.csdn.net/niushuai666/article/details/7002823
http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d
AC自动机专题训练链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=25605#overview 这里我提交的代码是公开的,可以看到
题目来源:http://www.notonlysuccess.com/index.php/aho-corasick-automaton/
写AC自动机的代码风格是向昀神学的,好简洁,写起来好棒的感觉。
1、HDU 2222 Keywords Search 最基本的入门题了
就是求目标串中出现了几个模式串。
很基础了。使用一个int型的end数组记录,查询一次。
//====================== // HDU 2222 // 求目标串中出现了几个模式串 //==================== #include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <queue> using namespace std; struct Trie { int next[500010][26],fail[500010],end[500010]; int root,L; int newnode() { for(int i = 0;i < 26;i++) next[L][i] = -1; end[L++] = 0; return L-1; } void init() { L = 0; root = newnode(); } void insert(char buf[]) { int len = strlen(buf); int now = root; for(int i = 0;i < len;i++) { if(next[now][buf[i]-'a'] == -1) next[now][buf[i]-'a'] = newnode(); now = next[now][buf[i]-'a']; } end[now]++; } void build() { queue<int>Q; fail[root] = root; for(int i = 0;i < 26;i++) if(next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; Q.push(next[root][i]); } while( !Q.empty() ) { int now = Q.front(); Q.pop(); for(int i = 0;i < 26;i++) if(next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]]=next[fail[now]][i]; Q.push(next[now][i]); } } } int query(char buf[]) { int len = strlen(buf); int now = root; int res = 0; for(int i = 0;i < len;i++) { now = next[now][buf[i]-'a']; int temp = now; while( temp != root ) { res += end[temp]; end[temp] = 0; temp = fail[temp]; } } return res; } void debug() { for(int i = 0;i < L;i++) { printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); for(int j = 0;j < 26;j++) printf("%2d",next[i][j]); printf("]\n"); } } }; char buf[1000010]; Trie ac; int main() { int T; int n; scanf("%d",&T); while( T-- ) { scanf("%d",&n); ac.init(); for(int i = 0;i < n;i++) { scanf("%s",buf); ac.insert(buf); } ac.build(); scanf("%s",buf); printf("%d\n",ac.query(buf)); } return 0; }
这题和上题差不多,要输出出现模式串的id,用end记录id就可以了。还有trie树的分支是128的
题解here
//============================================================================ // Name : HDU.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <queue> using namespace std; struct Trie { int next[210*500][128],fail[210*500],end[210*500]; int root,L; int newnode() { for(int i = 0;i < 128;i++) next[L][i] = -1; end[L++] = -1; return L-1; } void init() { L = 0; root = newnode(); } void insert(char s[],int id) { int len = strlen(s); int now = root; for(int i = 0;i < len;i++) { if(next[now][s[i]] == -1) next[now][s[i]] = newnode(); now=next[now][s[i]]; } end[now]=id; } void build() { queue<int>Q; fail[root] = root; for(int i = 0;i < 128;i++) if(next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; Q.push(next[root][i]); } while(!Q.empty()) { int now = Q.front(); Q.pop(); for(int i = 0;i < 128;i++) if(next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; Q.push(next[now][i]); } } } bool used[510]; bool query(char buf[],int n,int id) { int len = strlen(buf); int now = root; memset(used,false,sizeof(used)); bool flag = false; for(int i = 0;i < len;i++) { now = next[now][buf[i]]; int temp = now; while(temp != root) { if(end[temp] != -1) { used[end[temp]] = true; flag = true; } temp = fail[temp]; } } if(!flag)return false; printf("web %d:",id); for(int i = 1;i <= n;i++) if(used[i]) printf(" %d",i); printf("\n"); return true; } }; char buf[10010]; Trie ac; int main() { int n,m; while(scanf("%d",&n) != EOF) { ac.init(); for(int i = 1;i <= n;i++) { scanf("%s",buf); ac.insert(buf,i); } ac.build(); int ans = 0; scanf("%d",&m); for(int i = 1;i <= m;i++) { scanf("%s",buf); if(ac.query(buf,n,i)) ans++; } printf("total: %d\n",ans); } return 0; }
这题的变化也不大,就是需要输出每个模式串出现的次数,查询的时候使用一个数组进行记录就可以了
1 //============================================================================ 2 // Name : HDU.cpp 3 // Author : 4 // Version : 5 // Copyright : Your copyright notice 6 // Description : Hello World in C++, Ansi-style 7 //============================================================================ 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string.h> 12 #include <algorithm> 13 #include <queue> 14 using namespace std; 15 16 char str[1010][100]; 17 struct Trie 18 { 19 int next[1010*50][128],fail[1010*50],end[1010*50]; 20 int root,L; 21 int newnode() 22 { 23 for(int i = 0;i < 128;i++) 24 next[L][i] = -1; 25 end[L++] = -1; 26 return L-1; 27 } 28 void init() 29 { 30 L = 0; 31 root = newnode(); 32 } 33 void insert(char s[],int id) 34 { 35 int len = strlen(s); 36 int now = root; 37 for(int i = 0;i < len;i++) 38 { 39 if(next[now][s[i]] == -1) 40 next[now][s[i]] = newnode(); 41 now = next[now][s[i]]; 42 } 43 end[now] = id; 44 } 45 void build() 46 { 47 queue<int>Q; 48 fail[root] = root; 49 for(int i = 0;i < 128;i++) 50 if(next[root][i] == -1) 51 next[root][i] = root; 52 else 53 { 54 fail[next[root][i]] = root; 55 Q.push(next[root][i]); 56 } 57 while(!Q.empty()) 58 { 59 int now = Q.front(); 60 Q.pop(); 61 for(int i = 0;i < 128;i++) 62 if(next[now][i] == -1) 63 next[now][i]=next[fail[now]][i]; 64 else 65 { 66 fail[next[now][i]]=next[fail[now]][i]; 67 Q.push(next[now][i]); 68 } 69 } 70 } 71 int num[1010]; 72 void query(char buf[],int n) 73 { 74 for(int i = 0;i < n;i++) 75 num[i] = 0; 76 int len=strlen(buf); 77 int now=root; 78 for(int i=0;i<len;i++) 79 { 80 now=next[now][buf[i]]; 81 int temp = now; 82 while( temp != root ) 83 { 84 if(end[temp] != -1) 85 num[end[temp]]++; 86 temp = fail[temp]; 87 } 88 } 89 for(int i = 0;i < n;i++) 90 if(num[i] > 0) 91 printf("%s: %d\n",str[i],num[i]); 92 } 93 94 }; 95 96 char buf[2000010]; 97 Trie ac; 98 void debug() 99 { 100 for (int i = 0; i < ac.L; i++) 101 { 102 printf("id = %3d ,fail = %3d ,end = %3d, chi = [",i,ac.fail[i],ac.end[i]); 103 for (int j = 0; j < 128; j++) 104 printf("%2d ",ac.next[i][j]); 105 printf("]\n"); 106 } 107 } 108 int main() 109 { 110 // freopen("in.txt","r",stdin); 111 // freopen("out.txt","w",stdout); 112 int n; 113 while(scanf("%d",&n) == 1) 114 { 115 ac.init(); 116 for(int i = 0;i < n;i++) 117 { 118 scanf("%s",str[i]); 119 ac.insert(str[i],i); 120 } 121 ac.build(); 122 scanf("%s",buf); 123 ac.query(buf,n); 124 } 125 return 0; 126 }
主要是解码过程,解码以后就是模板题了。
求出现的模式串的种类数
分支需要256个
1 //============================================================================ 2 // Name : ZOJ.cpp 3 // Author : 4 // Version : 5 // Copyright : Your copyright notice 6 // Description : Hello World in C++, Ansi-style 7 //============================================================================ 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string.h> 12 #include <algorithm> 13 #include <queue> 14 using namespace std; 15 16 struct Trie 17 { 18 int next[520*64][256],fail[520*64],end[520*64]; 19 int root,L; 20 int newnode() 21 { 22 for(int i = 0;i < 256;i++) 23 next[L][i] = -1; 24 end[L++] = -1; 25 return L-1; 26 } 27 void init() 28 { 29 L = 0; 30 root = newnode(); 31 } 32 void insert(unsigned char buf[],int len,int id) 33 { 34 int now = root; 35 for(int i = 0;i < len;i++) 36 { 37 if(next[now][buf[i]] == -1) 38 next[now][buf[i]] = newnode(); 39 now = next[now][buf[i]]; 40 } 41 end[now] = id; 42 } 43 void build() 44 { 45 queue<int>Q; 46 fail[root] = root; 47 for(int i = 0;i < 256;i++) 48 if(next[root][i] == -1) 49 next[root][i] = root; 50 else 51 { 52 fail[next[root][i]]=root; 53 Q.push(next[root][i]); 54 } 55 while(!Q.empty()) 56 { 57 int now = Q.front(); 58 Q.pop(); 59 for(int i = 0;i < 256;i++) 60 if(next[now][i] == -1) 61 next[now][i] = next[fail[now]][i]; 62 else 63 { 64 fail[next[now][i]] = next[fail[now]][i]; 65 Q.push(next[now][i]); 66 } 67 } 68 } 69 bool used[520]; 70 int query(unsigned char buf[],int len,int n) 71 { 72 memset(used,false,sizeof(used)); 73 int now = root; 74 for(int i = 0;i < len;i++) 75 { 76 now = next[now][buf[i]]; 77 int temp = now; 78 while( temp!=root ) 79 { 80 if(end[temp] != -1) 81 used[end[temp]]=true; 82 temp = fail[temp]; 83 } 84 } 85 int res = 0; 86 for(int i = 0;i < n;i++) 87 if(used[i]) 88 res++; 89 return res; 90 } 91 }; 92 93 unsigned char buf[2050]; 94 int tot; 95 char str[4000]; 96 unsigned char s[4000]; 97 unsigned char Get(char ch) 98 { 99 if( ch>='A'&&ch<='Z' )return ch-'A'; 100 if( ch>='a'&&ch<='z' )return ch-'a'+26; 101 if( ch>='0'&&ch<='9' )return ch-'0'+52; 102 if( ch=='+' )return 62; 103 else return 63; 104 } 105 void change(unsigned char str[],int len) 106 { 107 int t=0; 108 for(int i=0;i<len;i+=4) 109 { 110 buf[t++]=((str[i]<<2)|(str[i+1]>>4)); 111 if(i+2 < len) 112 buf[t++]=( (str[i+1]<<4)|(str[i+2]>>2) ); 113 if(i+3 < len) 114 buf[t++]= ( (str[i+2]<<6)|str[i+3] ); 115 } 116 tot=t; 117 } 118 Trie ac; 119 int main() 120 { 121 // freopen("in.txt","r",stdin); 122 // freopen("out.txt","w",stdout); 123 int n,m; 124 while(scanf("%d",&n) == 1) 125 { 126 ac.init(); 127 for(int i = 0;i < n;i++) 128 { 129 scanf("%s",str); 130 int len = strlen(str); 131 while(str[len-1]=='=')len--; 132 for(int j = 0;j < len;j++) 133 { 134 s[j] = Get(str[j]); 135 } 136 change(s,len); 137 ac.insert(buf,tot,i); 138 } 139 ac.build(); 140 scanf("%d",&m); 141 while(m--) 142 { 143 scanf("%s",str); 144 int len=strlen(str); 145 while(str[len-1]=='=')len--; 146 for(int j = 0;j < len;j++) 147 s[j] = Get(str[j]); 148 change(s,len); 149 printf("%d\n",ac.query(buf,tot,n)); 150 } 151 printf("\n"); 152 } 153 return 0; 154 }
AC自动机+矩阵加速
这个时候AC自动机 的一种状态转移图的思路就很透彻了。
AC自动机就是可以确定状态的转移。
1 //============================================================================ 2 // Name : POJ.cpp 3 // Author : 4 // Version : 5 // Copyright : Your copyright notice 6 // Description : Hello World in C++, Ansi-style 7 //============================================================================ 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <algorithm> 12 #include <string.h> 13 #include <queue> 14 using namespace std; 15 16 const int MOD=100000; 17 struct Matrix 18 { 19 int mat[110][110],n; 20 Matrix(){} 21 Matrix(int _n) 22 { 23 n = _n; 24 for(int i=0;i<n;i++) 25 for(int j=0;j<n;j++) 26 mat[i][j]=0; 27 } 28 Matrix operator *(const Matrix &b)const 29 { 30 Matrix ret=Matrix(n); 31 for(int i=0;i<n;i++) 32 for(int j=0;j<n;j++) 33 for(int k=0;k<n;k++) 34 { 35 int tmp=(long long)mat[i][k]*b.mat[k][j]%MOD; 36 ret.mat[i][j]=(ret.mat[i][j]+tmp)%MOD; 37 } 38 return ret; 39 } 40 }; 41 struct Trie 42 { 43 int next[110][4],fail[110]; 44 bool end[110]; 45 int root,L; 46 int newnode() 47 { 48 for(int i=0;i<4;i++) 49 next[L][i]=-1; 50 end[L++]=false; 51 return L-1; 52 } 53 void init() 54 { 55 L=0; 56 root=newnode(); 57 } 58 int getch(char ch) 59 { 60 switch(ch) 61 { 62 case 'A': 63 return 0; 64 break; 65 case 'C': 66 return 1; 67 break; 68 case 'G': 69 return 2; 70 break; 71 case 'T': 72 return 3; 73 break; 74 } 75 } 76 void insert(char s[]) 77 { 78 int len=strlen(s); 79 int now=root; 80 for(int i = 0;i < len;i++) 81 { 82 if(next[now][getch(s[i])] == -1) 83 next[now][getch(s[i])] = newnode(); 84 now = next[now][getch(s[i])]; 85 } 86 end[now]=true; 87 } 88 void build() 89 { 90 queue<int>Q; 91 for(int i = 0;i < 4;i++) 92 if(next[root][i] == -1) 93 next[root][i] = root; 94 else 95 { 96 fail[next[root][i]] = root; 97 Q.push(next[root][i]); 98 } 99 while(!Q.empty()) 100 { 101 int now = Q.front(); 102 Q.pop(); 103 if(end[fail[now]]==true) 104 end[now]=true; 105 for(int i = 0;i < 4;i++) 106 { 107 if(next[now][i] == -1) 108 next[now][i] = next[fail[now]][i]; 109 else 110 { 111 fail[next[now][i]] = next[fail[now]][i]; 112 Q.push(next[now][i]); 113 } 114 } 115 } 116 } 117 Matrix getMatrix() 118 { 119 Matrix res = Matrix(L); 120 for(int i=0;i<L;i++) 121 for(int j=0;j<4;j++) 122 if(end[next[i][j]]==false) 123 res.mat[i][next[i][j]]++; 124 return res; 125 } 126 }; 127 128 Trie ac; 129 char buf[20]; 130 131 Matrix pow_M(Matrix a,int n) 132 { 133 Matrix ret = Matrix(a.n); 134 for(int i = 0; i < ret.n; i++) 135 ret.mat[i][i]=1; 136 Matrix tmp=a; 137 while(n) 138 { 139 if(n&1)ret=ret*tmp; 140 tmp=tmp*tmp; 141 n>>=1; 142 } 143 return ret; 144 } 145 146 int main() 147 { 148 int n,m; 149 while(scanf("%d%d",&n,&m) != EOF) 150 { 151 ac.init(); 152 for(int i=0;i<n;i++) 153 { 154 scanf("%s",buf); 155 ac.insert(buf); 156 } 157 ac.build(); 158 Matrix a=ac.getMatrix(); 159 a=pow_M(a,m); 160 int ans=0; 161 for(int i=0;i<a.n;i++) 162 { 163 ans=(ans+a.mat[0][i])%MOD; 164 } 165 printf("%d\n",ans); 166 } 167 return 0; 168 }
这题和上题有些类似。但是需要求和。
所以给
矩阵增加一维,这样可以完美解决
题解here
1 //============================================================================ 2 // Name : HDU.cpp 3 // Author : 4 // Version : 5 // Copyright : Your copyright notice 6 // Description : Hello World in C++, Ansi-style 7 //============================================================================ 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string.h> 12 #include <algorithm> 13 #include <queue> 14 using namespace std; 15 struct Matrix 16 { 17 unsigned long long mat[40][40]; 18 int n; 19 Matrix(){} 20 Matrix(int _n) 21 { 22 n=_n; 23 for(int i=0;i<n;i++) 24 for(int j=0;j<n;j++) 25 mat[i][j] = 0; 26 } 27 Matrix operator *(const Matrix &b)const 28 { 29 Matrix ret = Matrix(n); 30 for(int i=0;i<n;i++) 31 for(int j=0;j<n;j++) 32 for(int k=0;k<n;k++) 33 ret.mat[i][j]+=mat[i][k]*b.mat[k][j]; 34 return ret; 35 } 36 }; 37 unsigned long long pow_m(unsigned long long a,int n) 38 { 39 unsigned long long ret=1; 40 unsigned long long tmp = a; 41 while(n) 42 { 43 if(n&1)ret*=tmp; 44 tmp*=tmp; 45 n>>=1; 46 } 47 return ret; 48 } 49 Matrix pow_M(Matrix a,int n) 50 { 51 Matrix ret = Matrix(a.n); 52 for(int i=0;i<a.n;i++) 53 ret.mat[i][i] = 1; 54 Matrix tmp = a; 55 while(n) 56 { 57 if(n&1)ret=ret*tmp; 58 tmp=tmp*tmp; 59 n>>=1; 60 } 61 return ret; 62 } 63 struct Trie 64 { 65 int next[40][26],fail[40]; 66 bool end[40]; 67 int root,L; 68 int newnode() 69 { 70 for(int i = 0;i < 26;i++) 71 next[L][i] = -1; 72 end[L++] = false; 73 return L-1; 74 } 75 void init() 76 { 77 L = 0; 78 root = newnode(); 79 } 80 void insert(char buf[]) 81 { 82 int len = strlen(buf); 83 int now = root; 84 for(int i = 0;i < len;i++) 85 { 86 if(next[now][buf[i]-'a'] == -1) 87 next[now][buf[i]-'a'] = newnode(); 88 now = next[now][buf[i]-'a']; 89 } 90 end[now] = true; 91 } 92 void build() 93 { 94 queue<int>Q; 95 fail[root]=root; 96 for(int i = 0;i < 26;i++) 97 if(next[root][i] == -1) 98 next[root][i] = root; 99 else 100 { 101 fail[next[root][i]] = root; 102 Q.push(next[root][i]); 103 } 104 while(!Q.empty()) 105 { 106 int now = Q.front(); 107 Q.pop(); 108 if(end[fail[now]])end[now]=true; 109 for(int i = 0;i < 26;i++) 110 if(next[now][i] == -1) 111 next[now][i] = next[fail[now]][i]; 112 else 113 { 114 fail[next[now][i]] = next[fail[now]][i]; 115 Q.push(next[now][i]); 116 } 117 } 118 } 119 Matrix getMatrix() 120 { 121 Matrix ret = Matrix(L+1); 122 for(int i = 0;i < L;i++) 123 for(int j = 0;j < 26;j++) 124 if(end[next[i][j]]==false) 125 ret.mat[i][next[i][j]] ++; 126 for(int i = 0;i < L+1;i++) 127 ret.mat[i][L] = 1; 128 return ret; 129 } 130 void debug() 131 { 132 for(int i = 0;i < L;i++) 133 { 134 printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); 135 for(int j = 0;j < 26;j++) 136 printf("%2d",next[i][j]); 137 printf("]\n"); 138 } 139 } 140 }; 141 char buf[10]; 142 Trie ac; 143 int main() 144 { 145 // freopen("in.txt","r",stdin); 146 // freopen("out.txt","w",stdout); 147 int n,L; 148 while(scanf("%d%d",&n,&L)==2) 149 { 150 ac.init(); 151 for(int i = 0;i < n;i++) 152 { 153 scanf("%s",buf); 154 ac.insert(buf); 155 } 156 ac.build(); 157 Matrix a = ac.getMatrix(); 158 a = pow_M(a,L); 159 unsigned long long res = 0; 160 for(int i = 0;i < a.n;i++) 161 res += a.mat[0][i]; 162 res--; 163 164 /* 165 * f[n]=1 + 26^1 + 26^2 +...26^n 166 * f[n]=26*f[n-1]+1 167 * {f[n] 1} = {f[n-1] 1}[26 0;1 1] 168 * 数是f[L]-1; 169 * 此题的L<2^31.矩阵的幂不能是L+1次,否则就超时了 170 */ 171 a = Matrix(2); 172 a.mat[0][0]=26; 173 a.mat[1][0] = a.mat[1][1] = 1; 174 a=pow_M(a,L); 175 unsigned long long ans=a.mat[1][0]+a.mat[0][0]; 176 ans--; 177 ans-=res; 178 cout<<ans<<endl; 179 } 180 return 0; 181 }
AC自动机+DP+高精度
好题
题解here
//============================================================================ // Name : POJ.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> #include <queue> #include <map> using namespace std; map<char,int>mp; int N,M,P; struct Matrix { int mat[110][110]; int n; Matrix(){} Matrix(int _n) { n=_n; for(int i = 0;i < n;i++) for(int j = 0;j < n;j++) mat[i][j] = 0; } }; struct Trie { int next[110][256],fail[110]; bool end[110]; int L,root; int newnode() { for(int i = 0;i < 256;i++) next[L][i] = -1; end[L++] = false; return L-1; } void init() { L = 0; root = newnode(); } void insert(char buf[]) { int len = strlen(buf); int now = root; for(int i = 0;i < len;i++) { if(next[now][mp[buf[i]]] == -1) next[now][mp[buf[i]]] = newnode(); now = next[now][mp[buf[i]]]; } end[now] = true; } void build() { queue<int>Q; fail[root] = root; for(int i = 0;i < 256;i++) if(next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; Q.push(next[root][i]); } while(!Q.empty()) { int now = Q.front(); Q.pop(); if(end[fail[now]]==true)end[now]=true; for(int i = 0;i < 256;i++) if(next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; Q.push(next[now][i]); } } } Matrix getMatrix() { Matrix res = Matrix(L); for(int i = 0;i < L;i++) for(int j = 0;j < N;j++) if(end[next[i][j]]==false) res.mat[i][next[i][j]]++; return res; } void debug() { for(int i = 0;i < L;i++) { printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); for(int j = 0;j < 26;j++) printf("%2d",next[i][j]); printf("]\n"); } } }; /* * 高精度,支持乘法和加法 */ struct BigInt { const static int mod = 10000; const static int DLEN = 4; int a[600],len; BigInt() { memset(a,0,sizeof(a)); len = 1; } BigInt(int v) { memset(a,0,sizeof(a)); len = 0; do { a[len++] = v%mod; v /= mod; }while(v); } BigInt(const char s[]) { memset(a,0,sizeof(a)); int L = strlen(s); len = L/DLEN; if(L%DLEN)len++; int index = 0; for(int i = L-1;i >= 0;i -= DLEN) { int t = 0; int k = i - DLEN + 1; if(k < 0)k = 0; for(int j = k;j <= i;j++) t = t*10 + s[j] - '0'; a[index++] = t; } } BigInt operator +(const BigInt &b)const { BigInt res; res.len = max(len,b.len); for(int i = 0;i <= res.len;i++) res.a[i] = 0; for(int i = 0;i < res.len;i++) { res.a[i] += ((i < len)?a[i]:0)+((i < b.len)?b.a[i]:0); res.a[i+1] += res.a[i]/mod; res.a[i] %= mod; } if(res.a[res.len] > 0)res.len++; return res; } BigInt operator *(const BigInt &b)const { BigInt res; for(int i = 0; i < len;i++) { int up = 0; for(int j = 0;j < b.len;j++) { int temp = a[i]*b.a[j] + res.a[i+j] + up; res.a[i+j] = temp%mod; up = temp/mod; } if(up != 0) res.a[i + b.len] = up; } res.len = len + b.len; while(res.a[res.len - 1] == 0 &&res.len > 1)res.len--; return res; } void output() { printf("%d",a[len-1]); for(int i = len-2;i >=0 ;i--) printf("%04d",a[i]); printf("\n"); } }; char buf[1010]; BigInt dp[2][110]; Trie ac; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); while(scanf("%d%d%d",&N,&M,&P)==3) { gets(buf); gets(buf); mp.clear(); int len = strlen(buf); for(int i = 0;i < len;i++) mp[buf[i]]=i; ac.init(); for(int i = 0;i < P;i++) { gets(buf); ac.insert(buf); } ac.build(); // ac.debug(); Matrix a= ac.getMatrix(); // for(int i = 0;i <a.n;i++) // { // for(int j=0;j<a.n;j++)printf("%d ",a.mat[i][j]); // cout<<endl; // } int now = 0; dp[now][0] = 1; for(int i = 1;i < a.n;i++) dp[now][i] = 0; for(int i = 0;i < M;i++) { now^=1; for(int j = 0;j < a.n;j++) dp[now][j] = 0; for(int j = 0;j < a.n;j++) for(int k = 0;k < a.n;k++) if(a.mat[j][k] > 0) dp[now][k] = dp[now][k]+dp[now^1][j]*a.mat[j][k]; // for(int j = 0;j < a.n;j++) // dp[now][j].output(); } BigInt ans = 0; for(int i = 0;i < a.n;i++) ans = ans + dp[now][i]; ans.output(); } return 0; }
AC自动机+状态压缩DP
相当于在AC自动机上产生状态转移,然后进行dp
//============================================================================ // Name : HDU.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> using namespace std; const int MOD=20090717; int n,m,k; int dp[30][110][1<<10]; int num[5000]; struct Trie { int next[110][26],fail[110],end[110]; int root,L; int newnode() { for(int i = 0;i < 26;i++) next[L][i] = -1; end[L++] = 0; return L-1; } void init() { L = 0; root = newnode(); } void insert(char buf[],int id) { int len = strlen(buf); int now = root; for(int i = 0;i < len;i++) { if(next[now][buf[i]-'a']==-1) next[now][buf[i]-'a'] = newnode(); now = next[now][buf[i]-'a']; } end[now] |= (1<<id); } void build() { queue<int>Q; fail[root] = root; for(int i = 0;i < 26;i++) if(next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; Q.push(next[root][i]); } while(!Q.empty()) { int now = Q.front(); Q.pop(); end[now] |= end[fail[now]]; for(int i = 0;i < 26;i++) if(next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; Q.push(next[now][i]); } } } int solve() { //memset(dp,0,sizeof(dp)); for(int i = 0;i <= n;i++) for(int j = 0;j < L;j++) for(int p = 0;p < (1<<m);p++) dp[i][j][p]=0; dp[0][0][0] = 1; for(int i = 0;i < n;i++) for(int j = 0;j < L;j++) for(int p = 0;p< (1<<m);p++) if(dp[i][j][p] > 0) { for(int x = 0;x < 26;x++) { int newi = i+1; int newj = next[j][x]; int newp = (p|end[newj]); dp[newi][newj][newp] += dp[i][j][p]; dp[newi][newj][newp]%=MOD; } } int ans = 0; for(int p = 0;p < (1<<m);p++) { if(num[p] < k)continue; for(int i = 0;i < L;i++) { ans = (ans + dp[n][i][p])%MOD; } } return ans; } }; char buf[20]; Trie ac; int main() { for(int i=0;i<(1<<10);i++) { num[i] = 0; for(int j = 0;j < 10;j++) if(i & (1<<j)) num[i]++; } while(scanf("%d%d%d",&n,&m,&k)==3) { if(n== 0 && m==0 &&k==0)break; ac.init(); for(int i = 0;i < m;i++) { scanf("%s",buf); ac.insert(buf,i); } ac.build(); printf("%d\n",ac.solve()); } return 0; }
需要输出字典序最小的解
在DP的时候加一个字符数组来记录就行了
//============================================================================ // Name : HDU.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <string.h> #include <stdio.h> #include <algorithm> #include <queue> using namespace std; int a[110]; int dp[55][1110]; char str[55][1110][55]; bool cmp(char s1[],char s2[]) { int len1=strlen(s1); int len2=strlen(s2); if(len1 != len2)return len1 < len2; else return strcmp(s1,s2) < 0; } const int INF=0x3f3f3f3f; struct Trie { int next[1110][26],fail[1110],end[1110]; int root,L; int newnode() { for(int i = 0;i < 26;i++) next[L][i] = -1; end[L++] = -1; return L-1; } void init() { L = 0; root = newnode(); } void insert(char buf[],int id) { int len = strlen(buf); int now = root; for(int i = 0;i < len;i++) { if(next[now][buf[i]-'a'] == -1) next[now][buf[i]-'a'] = newnode(); now = next[now][buf[i]-'a']; } end[now] = id; } void build() { queue<int>Q; fail[root] = root; for(int i = 0;i < 26;i++) if(next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; Q.push(next[root][i]); } while(!Q.empty()) { int now = Q.front(); Q.pop(); for(int i = 0;i < 26;i++) if(next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; Q.push(next[now][i]); } } } void solve(int n) { for(int i = 0;i <= n;i++) for(int j = 0;j < L;j++) dp[i][j] = -INF; dp[0][0] = 0; strcpy(str[0][0],""); char ans[55]; strcpy(ans,""); int Max = 0; char tmp[55]; for(int i = 0; i < n;i++) for(int j = 0;j < L;j++) if(dp[i][j]>=0) { strcpy(tmp,str[i][j]); int len = strlen(tmp); for(int k = 0;k < 26;k++) { int nxt=next[j][k]; tmp[len] = 'a'+k; tmp[len+1] = 0; int tt = dp[i][j]; if(end[nxt] != -1) tt+=a[end[nxt]]; if(dp[i+1][nxt]<tt || (dp[i+1][nxt]==tt && cmp(tmp,str[i+1][nxt]))) { dp[i+1][nxt] = tt; strcpy(str[i+1][nxt],tmp); if(tt > Max ||(tt==Max && cmp(tmp,ans))) { Max = tt; strcpy(ans,tmp); } } } } printf("%s\n",ans); } }; char buf[60]; Trie ac; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; int n,m; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); ac.init(); for(int i = 0;i < m;i++) { scanf("%s",buf); ac.insert(buf,i); } for(int i = 0;i < m;i++) scanf("%d",&a[i]); ac.build(); ac.solve(n); } return 0; }
很简单的AC自动机+DP了
1 //============================================================================ 2 // Name : HDU.cpp 3 // Author : 4 // Version : 5 // Copyright : Your copyright notice 6 // Description : Hello World in C++, Ansi-style 7 //============================================================================ 8 9 #include <iostream> 10 #include <string.h> 11 #include <stdio.h> 12 #include <algorithm> 13 #include <queue> 14 using namespace std; 15 const int INF = 0x3f3f3f3f; 16 struct Trie 17 { 18 int next[1010][4],fail[1010]; 19 bool end[1010]; 20 int root,L; 21 int newnode() 22 { 23 for(int i = 0;i < 4;i++) 24 next[L][i] = -1; 25 end[L++] = false; 26 return L-1; 27 } 28 void init() 29 { 30 L = 0; 31 root = newnode(); 32 } 33 int getch(char ch) 34 { 35 if(ch == 'A')return 0; 36 else if(ch == 'C')return 1; 37 else if(ch == 'G')return 2; 38 else if(ch == 'T')return 3; 39 } 40 void insert(char buf[]) 41 { 42 int len = strlen(buf); 43 int now = root; 44 for(int i = 0;i < len;i++) 45 { 46 if(next[now][getch(buf[i])] == -1) 47 next[now][getch(buf[i])] = newnode(); 48 now = next[now][getch(buf[i])]; 49 } 50 end[now] = true; 51 } 52 void build() 53 { 54 queue<int>Q; 55 fail[root] = root; 56 for(int i = 0;i < 4;i++) 57 if(next[root][i] == -1) 58 next[root][i] = root; 59 else 60 { 61 fail[next[root][i]] = root; 62 Q.push(next[root][i]); 63 } 64 while(!Q.empty()) 65 { 66 int now = Q.front(); 67 Q.pop(); 68 if(end[fail[now]])end[now] = true;//这里不要忘记 69 for(int i = 0;i < 4;i++) 70 if(next[now][i] == -1) 71 next[now][i] = next[fail[now]][i]; 72 else 73 { 74 fail[next[now][i]] = next[fail[now]][i]; 75 Q.push(next[now][i]); 76 } 77 } 78 } 79 int dp[1010][1010]; 80 int solve(char buf[]) 81 { 82 int len = strlen(buf); 83 for(int i = 0;i <= len;i++) 84 for(int j = 0;j < L;j++) 85 dp[i][j] = INF; 86 dp[0][root] = 0; 87 for(int i = 0;i < len;i++) 88 for(int j = 0;j < L;j++) 89 if(dp[i][j] < INF) 90 { 91 for(int k = 0;k < 4;k++) 92 { 93 int news = next[j][k]; 94 if(end[news])continue; 95 int tmp; 96 if( k == getch(buf[i]))tmp = dp[i][j]; 97 else tmp = dp[i][j] + 1; 98 dp[i+1][news] = min(dp[i+1][news],tmp); 99 } 100 } 101 int ans = INF; 102 for(int j = 0;j < L;j++) 103 ans = min(ans,dp[len][j]); 104 if(ans == INF)ans = -1; 105 return ans; 106 } 107 108 }; 109 char buf[1010]; 110 Trie ac; 111 int main() 112 { 113 int n; 114 int iCase = 0; 115 while ( scanf("%d",&n) == 1 && n) 116 { 117 iCase++; 118 ac.init(); 119 while(n--) 120 { 121 scanf("%s",buf); 122 ac.insert(buf); 123 } 124 ac.build(); 125 scanf("%s",buf); 126 printf("Case %d: %d\n",iCase,ac.solve(buf)); 127 } 128 return 0; 129 }
11、ZOJ 3228 Searching the String
这题需要查询两种,一种是可重叠,一种是不可重叠的。
找模式串在目标串中的出现次数。
加一个数组记录上一次出现的位置,然后就可以求出不可重叠的了
1 //============================================================================ 2 // Name : ZOJ.cpp 3 // Author : 4 // Version : 5 // Copyright : Your copyright notice 6 // Description : Hello World in C++, Ansi-style 7 //============================================================================ 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string.h> 12 #include <algorithm> 13 #include <queue> 14 using namespace std; 15 16 struct Trie 17 { 18 int next[600010][26],fail[600010],deep[600010]; 19 int root,L; 20 int newnode() 21 { 22 for(int i = 0;i < 26;i++) 23 next[L][i] = -1; 24 L++; 25 return L-1; 26 } 27 void init() 28 { 29 L = 0; 30 root = newnode(); 31 deep[root] = 0; 32 } 33 int insert(char buf[]) 34 { 35 int len = strlen(buf); 36 int now = root; 37 for(int i = 0;i < len;i++) 38 { 39 if(next[now][buf[i]-'a'] == -1) 40 { 41 next[now][buf[i]-'a'] = newnode(); 42 deep[ next[now][buf[i]-'a'] ] = i+1; 43 } 44 now = next[now][buf[i]-'a']; 45 } 46 return now; 47 } 48 void build() 49 { 50 queue<int>Q; 51 fail[root] = root; 52 for(int i = 0;i < 26;i++) 53 if(next[root][i] == -1) 54 next[root][i] = root; 55 else 56 { 57 fail[next[root][i]] = root; 58 Q.push(next[root][i]); 59 } 60 while(!Q.empty()) 61 { 62 int now = Q.front(); 63 Q.pop(); 64 for(int i = 0;i < 26;i++) 65 if(next[now][i] == -1) 66 next[now][i] = next[fail[now]][i]; 67 else 68 { 69 fail[next[now][i]] = next[fail[now]][i]; 70 Q.push(next[now][i]); 71 } 72 } 73 } 74 int cnt[600010][2]; 75 int last[600010]; 76 void query(char buf[]) 77 { 78 int len = strlen(buf); 79 int now = root; 80 memset(cnt,0,sizeof(cnt)); 81 memset(last,-1,sizeof(last)); 82 for(int i = 0;i < len;i++) 83 { 84 now = next[now][buf[i]-'a']; 85 int temp = now; 86 while(temp != root) 87 { 88 cnt[temp][0]++; 89 if(i - last[temp] >= deep[temp]) 90 { 91 last[temp] = i; 92 cnt[temp][1]++; 93 } 94 temp = fail[temp]; 95 } 96 } 97 } 98 }; 99 Trie ac; 100 char str[100010]; 101 char buf[20]; 102 int typ[100010],pos[100010]; 103 int main() 104 { 105 // freopen("in.txt","r",stdin); 106 // freopen("out.txt","w",stdout); 107 int n; 108 int iCase = 0; 109 while(scanf("%s",str)==1) 110 { 111 iCase++; 112 printf("Case %d\n",iCase); 113 scanf("%d",&n); 114 ac.init(); 115 for(int i = 0;i < n;i++) 116 { 117 scanf("%d%s",&typ[i],buf); 118 pos[i]=ac.insert(buf); 119 } 120 ac.build(); 121 ac.query(str); 122 for(int i = 0;i < n;i++) 123 printf("%d\n",ac.cnt[pos[i]][typ[i]]); 124 printf("\n"); 125 } 126 return 0; 127 }
这题主要是状态的表示,就是记录ACGT出现的次数。
然后这个ACGT次数表示的时候,状态要转化。
题解here
1 //============================================================================ 2 // Name : HDU.cpp 3 // Author : 4 // Version : 5 // Copyright : Your copyright notice 6 // Description : Hello World in C++, Ansi-style 7 //============================================================================ 8 9 #include <iostream> 10 #include <string.h> 11 #include <stdio.h> 12 #include <algorithm> 13 #include <queue> 14 using namespace std; 15 const int INF = 0x3f3f3f3f; 16 struct Trie 17 { 18 int next[510][4],fail[510]; 19 int end[510]; 20 int root,L; 21 int newnode() 22 { 23 for(int i = 0;i < 4;i++) 24 next[L][i] = -1; 25 end[L++] = 0; 26 return L-1; 27 } 28 void init() 29 { 30 L = 0; 31 root = newnode(); 32 } 33 int getch(char ch) 34 { 35 if(ch == 'A')return 0; 36 else if(ch == 'C')return 1; 37 else if(ch == 'G')return 2; 38 else return 3; 39 } 40 void insert(char buf[]) 41 { 42 int len = strlen(buf); 43 int now = root; 44 for(int i = 0;i < len;i++) 45 { 46 if(next[now][getch(buf[i])] == -1) 47 next[now][getch(buf[i])] = newnode(); 48 now = next[now][getch(buf[i])]; 49 } 50 end[now] ++; 51 } 52 void build() 53 { 54 queue<int>Q; 55 fail[root] = root; 56 for(int i = 0;i < 4;i++) 57 if(next[root][i] == -1) 58 next[root][i] = root; 59 else 60 { 61 fail[next[root][i]] = root; 62 Q.push(next[root][i]); 63 } 64 while(!Q.empty()) 65 { 66 int now = Q.front(); 67 Q.pop(); 68 end[now] += end[fail[now]];/********/ 69 for(int i = 0;i < 4;i++) 70 if(next[now][i] == -1) 71 next[now][i] = next[fail[now]][i]; 72 else 73 { 74 fail[next[now][i]] = next[fail[now]][i]; 75 Q.push(next[now][i]); 76 } 77 } 78 } 79 int dp[510][11*11*11*11+5]; 80 int bit[4]; 81 int num[4]; 82 int solve(char buf[]) 83 { 84 int len = strlen(buf); 85 memset(num,0,sizeof(num)); 86 for(int i = 0;i < len;i++) 87 num[getch(buf[i])]++; 88 bit[0] = (num[1]+1)*(num[2]+1)*(num[3]+1); 89 bit[1] = (num[2]+1)*(num[3]+1); 90 bit[2] = (num[3]+1); 91 bit[3] = 1; 92 memset(dp,-1,sizeof(dp)); 93 dp[root][0] = 0; 94 for(int A = 0;A <= num[0];A++) 95 for(int B = 0;B <= num[1];B++) 96 for(int C = 0;C <= num[2];C++) 97 for(int D = 0;D <= num[3];D++) 98 { 99 int s = A*bit[0] + B*bit[1] + C*bit[2] + D*bit[3]; 100 for(int i = 0;i < L;i++) 101 if(dp[i][s] >= 0) 102 { 103 for(int k = 0;k < 4;k++) 104 { 105 if(k == 0 && A == num[0])continue; 106 if(k == 1 && B == num[1])continue; 107 if(k == 2 && C == num[2])continue; 108 if(k == 3 && D == num[3])continue; 109 dp[next[i][k]][s+bit[k]] = max(dp[next[i][k]][s+bit[k]],dp[i][s]+end[next[i][k]]); 110 } 111 } 112 } 113 int ans = 0; 114 int status = num[0]*bit[0] + num[1]*bit[1] + num[2]*bit[2] + num[3]*bit[3]; 115 for(int i = 0;i < L;i++) 116 ans = max(ans,dp[i][status]); 117 return ans; 118 } 119 }; 120 char buf[50]; 121 Trie ac; 122 int main() 123 { 124 // freopen("in.txt","r",stdin); 125 // freopen("out.txt","w",stdout); 126 int n; 127 int iCase = 0; 128 while( scanf("%d",&n) == 1 && n) 129 { 130 iCase++; 131 ac.init(); 132 for(int i = 0;i < n;i++) 133 { 134 scanf("%s",buf); 135 ac.insert(buf); 136 } 137 ac.build(); 138 scanf("%s",buf); 139 printf("Case %d: %d\n",iCase,ac.solve(buf)); 140 } 141 return 0; 142 }
使用最短路预处理出状态的转移。这样可以优化很多
1 //============================================================================ 2 // Name : HDU.cpp 3 // Author : 4 // Version : 5 // Copyright : Your copyright notice 6 // Description : Hello World in C++, Ansi-style 7 //============================================================================ 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string.h> 12 #include <algorithm> 13 #include <queue> 14 using namespace std; 15 16 const int INF = 0x3f3f3f3f; 17 18 struct Trie 19 { 20 int next[60010][2],fail[60010],end[60010]; 21 int root,L; 22 int newnode() 23 { 24 for(int i = 0;i < 2;i++) 25 next[L][i] = -1; 26 end[L++] = 0; 27 return L-1; 28 } 29 void init() 30 { 31 L = 0; 32 root = newnode(); 33 } 34 void insert(char buf[],int id) 35 { 36 int len = strlen(buf); 37 int now = root; 38 for(int i = 0;i < len ;i++) 39 { 40 if(next[now][buf[i]-'0'] == -1) 41 next[now][buf[i]-'0'] = newnode(); 42 now = next[now][buf[i]-'0']; 43 } 44 end[now] = id; 45 } 46 void build() 47 { 48 queue<int>Q; 49 fail[root] = root; 50 for(int i = 0;i < 2;i++) 51 if(next[root][i] == -1) 52 next[root][i] = root; 53 else 54 { 55 fail[next[root][i]] = root; 56 Q.push(next[root][i]); 57 } 58 while( !Q.empty() ) 59 { 60 int now = Q.front(); 61 Q.pop(); 62 if(end[fail[now]] == -1)end[now] = -1; 63 else end[now] |= end[fail[now]]; 64 for(int i = 0;i < 2;i++) 65 if(next[now][i] == -1) 66 next[now][i] = next[fail[now]][i]; 67 else 68 { 69 fail[next[now][i]] = next[fail[now]][i]; 70 Q.push(next[now][i]); 71 } 72 } 73 } 74 int g[11][11]; 75 int dp[1025][11]; 76 int cnt; 77 int pos[11]; 78 int dis[60010]; 79 80 81 void bfs(int k) 82 { 83 queue<int>q; 84 memset(dis,-1,sizeof(dis)); 85 dis[pos[k]] = 0; 86 q.push(pos[k]); 87 while(!q.empty()) 88 { 89 int now = q.front(); 90 q.pop(); 91 for(int i = 0; i< 2;i++) 92 { 93 int tmp = next[now][i]; 94 if(dis[tmp]<0 && end[tmp] >= 0) 95 { 96 dis[tmp] = dis[now] + 1; 97 q.push(tmp); 98 } 99 } 100 } 101 for(int i = 0;i < cnt;i++) 102 g[k][i] = dis[pos[i]]; 103 } 104 105 106 int solve(int n) 107 { 108 109 pos[0] = 0; 110 cnt = 1; 111 for(int i = 0;i < L;i++) 112 if(end[i] > 0) 113 pos[cnt++] = i; 114 for(int i = 0; i < cnt;i++) 115 bfs(i); 116 117 for(int i = 0;i < (1<<n);i++) 118 for(int j = 0;j < cnt;j++) 119 dp[i][j] = INF; 120 dp[0][0] = 0; 121 for(int i = 0;i <(1<<n);i++) 122 for(int j = 0;j < cnt;j++) 123 if(dp[i][j]<INF) 124 { 125 for(int k = 0;k < cnt;k++) 126 { 127 if(g[j][k] < 0)continue; 128 if( j == k)continue; 129 dp[i|end[pos[k]]][k] = min(dp[i|end[pos[k]]][k],dp[i][j]+g[j][k]); 130 } 131 } 132 int ans = INF; 133 for(int j = 0;j < cnt;j++) 134 ans = min(ans,dp[(1<<n)-1][j]); 135 return ans; 136 } 137 }; 138 Trie ac; 139 char buf[1010]; 140 141 int main() 142 { 143 // freopen("in.txt","r",stdin); 144 // freopen("out.txt","w",stdout); 145 int n,m; 146 while(scanf("%d%d",&n,&m) == 2) 147 { 148 if(n == 0 && m == 0)break; 149 ac.init(); 150 for(int i = 0;i < n;i++) 151 { 152 scanf("%s",buf); 153 ac.insert(buf,1<<i); 154 } 155 for(int i = 0;i < m;i++) 156 { 157 scanf("%s",buf); 158 ac.insert(buf,-1); 159 } 160 ac.build(); 161 printf("%d\n",ac.solve(n)); 162 } 163 return 0; 164 }
这道题很神,数位DP和AC自动机结合,太强大了。
题解here
1 //============================================================================ 2 // Name : ZOJ.cpp 3 // Author : 4 // Version : 5 // Copyright : Your copyright notice 6 // Description : Hello World in C++, Ansi-style 7 //============================================================================ 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string.h> 12 #include <algorithm> 13 #include <queue> 14 using namespace std; 15 16 struct Trie 17 { 18 int next[2010][2],fail[2010]; 19 bool end[2010]; 20 int root,L; 21 int newnode() 22 { 23 for(int i = 0;i < 2;i++) 24 next[L][i] = -1; 25 end[L++] = false; 26 return L-1; 27 } 28 void init() 29 { 30 L = 0; 31 root = newnode(); 32 } 33 void insert(char buf[]) 34 { 35 int len = strlen(buf); 36 int now = root; 37 for(int i = 0;i < len ;i++) 38 { 39 if(next[now][buf[i]-'0'] == -1) 40 next[now][buf[i]-'0'] = newnode(); 41 now = next[now][buf[i]-'0']; 42 } 43 end[now] = true; 44 } 45 void build() 46 { 47 queue<int>Q; 48 fail[root] = root; 49 for(int i = 0;i < 2;i++) 50 if(next[root][i] == -1) 51 next[root][i] = root; 52 else 53 { 54 fail[next[root][i]] = root; 55 Q.push(next[root][i]); 56 } 57 while(!Q.empty()) 58 { 59 int now = Q.front(); 60 Q.pop(); 61 if(end[fail[now]])end[now] = true; 62 for(int i = 0;i < 2;i++) 63 if(next[now][i] == -1) 64 next[now][i] = next[fail[now]][i]; 65 else 66 { 67 fail[next[now][i]] = next[fail[now]][i]; 68 Q.push(next[now][i]); 69 } 70 } 71 } 72 }; 73 Trie ac; 74 75 int bcd[2010][10]; 76 int change(int pre,int num) 77 { 78 if(ac.end[pre])return -1; 79 int cur = pre; 80 for(int i = 3;i >= 0;i--) 81 { 82 if(ac.end[ac.next[cur][(num>>i)&1]])return -1; 83 cur = ac.next[cur][(num>>i)&1]; 84 } 85 return cur; 86 } 87 void pre_init() 88 { 89 for(int i = 0;i <ac.L;i++) 90 for(int j = 0;j <10;j++) 91 bcd[i][j] = change(i,j); 92 } 93 const int MOD = 1000000009; 94 long long dp[210][2010]; 95 int bit[210]; 96 97 long long dfs(int pos,int s,bool flag,bool z) 98 { 99 if(pos == -1)return 1; 100 if(!flag && dp[pos][s]!=-1)return dp[pos][s]; 101 long long ans = 0; 102 if(z) 103 { 104 ans += dfs(pos-1,s,flag && bit[pos]==0,true); 105 ans %= MOD; 106 } 107 else 108 { 109 if(bcd[s][0]!=-1)ans += dfs(pos-1,bcd[s][0],flag && bit[pos]==0,false); 110 ans %= MOD; 111 } 112 int end = flag?bit[pos]:9; 113 for(int i = 1;i<=end;i++) 114 { 115 if(bcd[s][i]!=-1) 116 { 117 ans += dfs(pos-1,bcd[s][i],flag&&i==end,false); 118 ans %=MOD; 119 } 120 } 121 if(!flag && !z)dp[pos][s] = ans; 122 return ans; 123 } 124 125 long long calc(char s[]) 126 { 127 int len = strlen(s); 128 for(int i = 0;i < len;i++) 129 bit[i] = s[len-1-i]-'0'; 130 return dfs(len-1,0,1,1); 131 } 132 char str[210]; 133 int main() 134 { 135 // freopen("in.txt","r",stdin); 136 // freopen("out.txt","w",stdout); 137 int T; 138 scanf("%d",&T); 139 int n; 140 while(T--) 141 { 142 ac.init(); 143 scanf("%d",&n); 144 for(int i = 0;i < n;i++) 145 { 146 scanf("%s",str); 147 ac.insert(str); 148 } 149 ac.build(); 150 pre_init(); 151 memset(dp,-1,sizeof(dp)); 152 int ans = 0; 153 scanf("%s",str); 154 int len = strlen(str); 155 for(int i = len -1;i >=0;i--) 156 { 157 if(str[i]>'0') 158 { 159 str[i]--; 160 break; 161 } 162 else str[i] = '9'; 163 } 164 ans -= calc(str); 165 ans %=MOD; 166 scanf("%s",str); 167 ans += calc(str); 168 ans %=MOD; 169 if(ans < 0)ans += MOD; 170 printf("%d\n",ans); 171 } 172 return 0; 173 }