有了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("] "); } } }; 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 ",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(" "); 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 ",ans); } return 0; }
这题的变化也不大,就是需要输出每个模式串出现的次数,查询的时候使用一个数组进行记录就可以了
//============================================================================ // 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; char str[1010][100]; struct Trie { int next[1010*50][128],fail[1010*50],end[1010*50]; 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]); } } } int num[1010]; void query(char buf[],int n) { for(int i = 0;i < n;i++) num[i] = 0; int len=strlen(buf); int now=root; for(int i=0;i<len;i++) { now=next[now][buf[i]]; int temp = now; while( temp != root ) { if(end[temp] != -1) num[end[temp]]++; temp = fail[temp]; } } for(int i = 0;i < n;i++) if(num[i] > 0) printf("%s: %d ",str[i],num[i]); } }; char buf[2000010]; Trie ac; void debug() { for (int i = 0; i < ac.L; i++) { printf("id = %3d ,fail = %3d ,end = %3d, chi = [",i,ac.fail[i],ac.end[i]); for (int j = 0; j < 128; j++) printf("%2d ",ac.next[i][j]); printf("] "); } } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int n; while(scanf("%d",&n) == 1) { ac.init(); for(int i = 0;i < n;i++) { scanf("%s",str[i]); ac.insert(str[i],i); } ac.build(); scanf("%s",buf); ac.query(buf,n); } return 0; }
主要是解码过程,解码以后就是模板题了。
求出现的模式串的种类数
分支需要256个
//============================================================================ // Name : ZOJ.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; struct Trie { int next[520*64][256],fail[520*64],end[520*64]; int root,L; int newnode() { for(int i = 0;i < 256;i++) next[L][i] = -1; end[L++] = -1; return L-1; } void init() { L = 0; root = newnode(); } void insert(unsigned char buf[],int len,int id) { int now = root; for(int i = 0;i < len;i++) { if(next[now][buf[i]] == -1) next[now][buf[i]] = newnode(); now = next[now][buf[i]]; } end[now] = id; } 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(); 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]); } } } bool used[520]; int query(unsigned char buf[],int len,int n) { memset(used,false,sizeof(used)); int now = root; 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; temp = fail[temp]; } } int res = 0; for(int i = 0;i < n;i++) if(used[i]) res++; return res; } }; unsigned char buf[2050]; int tot; char str[4000]; unsigned char s[4000]; unsigned char Get(char ch) { if( ch>='A'&&ch<='Z' )return ch-'A'; if( ch>='a'&&ch<='z' )return ch-'a'+26; if( ch>='0'&&ch<='9' )return ch-'0'+52; if( ch=='+' )return 62; else return 63; } void change(unsigned char str[],int len) { int t=0; for(int i=0;i<len;i+=4) { buf[t++]=((str[i]<<2)|(str[i+1]>>4)); if(i+2 < len) buf[t++]=( (str[i+1]<<4)|(str[i+2]>>2) ); if(i+3 < len) buf[t++]= ( (str[i+2]<<6)|str[i+3] ); } tot=t; } Trie ac; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int n,m; while(scanf("%d",&n) == 1) { ac.init(); for(int i = 0;i < n;i++) { scanf("%s",str); int len = strlen(str); while(str[len-1]=='=')len--; for(int j = 0;j < len;j++) { s[j] = Get(str[j]); } change(s,len); ac.insert(buf,tot,i); } ac.build(); scanf("%d",&m); while(m--) { scanf("%s",str); int len=strlen(str); while(str[len-1]=='=')len--; for(int j = 0;j < len;j++) s[j] = Get(str[j]); change(s,len); printf("%d ",ac.query(buf,tot,n)); } printf(" "); } return 0; }
AC自动机+矩阵加速
这个时候AC自动机 的一种状态转移图的思路就很透彻了。
AC自动机就是可以确定状态的转移。
//============================================================================ // Name : POJ.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <stdio.h> #include <algorithm> #include <string.h> #include <queue> using namespace std; const int MOD=100000; struct Matrix { int mat[110][110],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; } Matrix operator *(const Matrix &b)const { Matrix ret=Matrix(n); for(int i=0;i<n;i++) for(int j=0;j<n;j++) for(int k=0;k<n;k++) { int tmp=(long long)mat[i][k]*b.mat[k][j]%MOD; ret.mat[i][j]=(ret.mat[i][j]+tmp)%MOD; } return ret; } }; struct Trie { int next[110][4],fail[110]; bool end[110]; int root,L; int newnode() { for(int i=0;i<4;i++) next[L][i]=-1; end[L++]=false; return L-1; } void init() { L=0; root=newnode(); } int getch(char ch) { switch(ch) { case 'A': return 0; break; case 'C': return 1; break; case 'G': return 2; break; case 'T': return 3; break; } } void insert(char s[]) { int len=strlen(s); int now=root; for(int i = 0;i < len;i++) { if(next[now][getch(s[i])] == -1) next[now][getch(s[i])] = newnode(); now = next[now][getch(s[i])]; } end[now]=true; } void build() { queue<int>Q; for(int i = 0;i < 4;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 < 4;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<4;j++) if(end[next[i][j]]==false) res.mat[i][next[i][j]]++; return res; } }; Trie ac; char buf[20]; Matrix pow_M(Matrix a,int n) { Matrix ret = Matrix(a.n); for(int i = 0; i < ret.n; i++) ret.mat[i][i]=1; Matrix tmp=a; while(n) { if(n&1)ret=ret*tmp; tmp=tmp*tmp; n>>=1; } return ret; } int main() { int n,m; while(scanf("%d%d",&n,&m) != EOF) { ac.init(); for(int i=0;i<n;i++) { scanf("%s",buf); ac.insert(buf); } ac.build(); Matrix a=ac.getMatrix(); a=pow_M(a,m); int ans=0; for(int i=0;i<a.n;i++) { ans=(ans+a.mat[0][i])%MOD; } printf("%d ",ans); } return 0; }
这题和上题有些类似。但是需要求和。
所以给矩阵增加一维,这样可以完美解决
题解here
//============================================================================ // 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; struct Matrix { unsigned long long mat[40][40]; 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; } Matrix operator *(const Matrix &b)const { Matrix ret = Matrix(n); for(int i=0;i<n;i++) for(int j=0;j<n;j++) for(int k=0;k<n;k++) ret.mat[i][j]+=mat[i][k]*b.mat[k][j]; return ret; } }; unsigned long long pow_m(unsigned long long a,int n) { unsigned long long ret=1; unsigned long long tmp = a; while(n) { if(n&1)ret*=tmp; tmp*=tmp; n>>=1; } return ret; } Matrix pow_M(Matrix a,int n) { Matrix ret = Matrix(a.n); for(int i=0;i<a.n;i++) ret.mat[i][i] = 1; Matrix tmp = a; while(n) { if(n&1)ret=ret*tmp; tmp=tmp*tmp; n>>=1; } return ret; } struct Trie { int next[40][26],fail[40]; bool end[40]; int root,L; int newnode() { for(int i = 0;i < 26;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][buf[i]-'a'] == -1) next[now][buf[i]-'a'] = newnode(); now = next[now][buf[i]-'a']; } end[now] = true; } 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(); if(end[fail[now]])end[now]=true; 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]); } } } Matrix getMatrix() { Matrix ret = Matrix(L+1); for(int i = 0;i < L;i++) for(int j = 0;j < 26;j++) if(end[next[i][j]]==false) ret.mat[i][next[i][j]] ++; for(int i = 0;i < L+1;i++) ret.mat[i][L] = 1; return ret; } 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("] "); } } }; char buf[10]; Trie ac; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int n,L; while(scanf("%d%d",&n,&L)==2) { ac.init(); for(int i = 0;i < n;i++) { scanf("%s",buf); ac.insert(buf); } ac.build(); Matrix a = ac.getMatrix(); a = pow_M(a,L); unsigned long long res = 0; for(int i = 0;i < a.n;i++) res += a.mat[0][i]; res--; /* * f[n]=1 + 26^1 + 26^2 +...26^n * f[n]=26*f[n-1]+1 * {f[n] 1} = {f[n-1] 1}[26 0;1 1] * 数是f[L]-1; * 此题的L<2^31.矩阵的幂不能是L+1次,否则就超时了 */ a = Matrix(2); a.mat[0][0]=26; a.mat[1][0] = a.mat[1][1] = 1; a=pow_M(a,L); unsigned long long ans=a.mat[1][0]+a.mat[0][0]; ans--; ans-=res; cout<<ans<<endl; } return 0; }
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("] "); } } }; /* * 高精度,支持乘法和加法 */ 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(" "); } }; 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 ",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 ",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了
//============================================================================ // 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; const int INF = 0x3f3f3f3f; struct Trie { int next[1010][4],fail[1010]; bool end[1010]; int root,L; int newnode() { for(int i = 0;i < 4;i++) next[L][i] = -1; end[L++] = false; return L-1; } void init() { L = 0; root = newnode(); } int getch(char ch) { if(ch == 'A')return 0; else if(ch == 'C')return 1; else if(ch == 'G')return 2; else if(ch == 'T')return 3; } void insert(char buf[]) { int len = strlen(buf); int now = root; for(int i = 0;i < len;i++) { if(next[now][getch(buf[i])] == -1) next[now][getch(buf[i])] = newnode(); now = next[now][getch(buf[i])]; } end[now] = true; } void build() { queue<int>Q; fail[root] = root; for(int i = 0;i < 4;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]])end[now] = true;//这里不要忘记 for(int i = 0;i < 4;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 dp[1010][1010]; int solve(char buf[]) { int len = strlen(buf); for(int i = 0;i <= len;i++) for(int j = 0;j < L;j++) dp[i][j] = INF; dp[0][root] = 0; for(int i = 0;i < len;i++) for(int j = 0;j < L;j++) if(dp[i][j] < INF) { for(int k = 0;k < 4;k++) { int news = next[j][k]; if(end[news])continue; int tmp; if( k == getch(buf[i]))tmp = dp[i][j]; else tmp = dp[i][j] + 1; dp[i+1][news] = min(dp[i+1][news],tmp); } } int ans = INF; for(int j = 0;j < L;j++) ans = min(ans,dp[len][j]); if(ans == INF)ans = -1; return ans; } }; char buf[1010]; Trie ac; int main() { int n; int iCase = 0; while ( scanf("%d",&n) == 1 && n) { iCase++; ac.init(); while(n--) { scanf("%s",buf); ac.insert(buf); } ac.build(); scanf("%s",buf); printf("Case %d: %d ",iCase,ac.solve(buf)); } return 0; }
11、ZOJ 3228 Searching the String
这题需要查询两种,一种是可重叠,一种是不可重叠的。
找模式串在目标串中的出现次数。
加一个数组记录上一次出现的位置,然后就可以求出不可重叠的了
//============================================================================ // Name : ZOJ.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; struct Trie { int next[600010][26],fail[600010],deep[600010]; int root,L; int newnode() { for(int i = 0;i < 26;i++) next[L][i] = -1; L++; return L-1; } void init() { L = 0; root = newnode(); deep[root] = 0; } int 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(); deep[ next[now][buf[i]-'a'] ] = i+1; } now = next[now][buf[i]-'a']; } return 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 cnt[600010][2]; int last[600010]; void query(char buf[]) { int len = strlen(buf); int now = root; memset(cnt,0,sizeof(cnt)); memset(last,-1,sizeof(last)); for(int i = 0;i < len;i++) { now = next[now][buf[i]-'a']; int temp = now; while(temp != root) { cnt[temp][0]++; if(i - last[temp] >= deep[temp]) { last[temp] = i; cnt[temp][1]++; } temp = fail[temp]; } } } }; Trie ac; char str[100010]; char buf[20]; int typ[100010],pos[100010]; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int n; int iCase = 0; while(scanf("%s",str)==1) { iCase++; printf("Case %d ",iCase); scanf("%d",&n); ac.init(); for(int i = 0;i < n;i++) { scanf("%d%s",&typ[i],buf); pos[i]=ac.insert(buf); } ac.build(); ac.query(str); for(int i = 0;i < n;i++) printf("%d ",ac.cnt[pos[i]][typ[i]]); printf(" "); } return 0; }
这题主要是状态的表示,就是记录ACGT出现的次数。
然后这个ACGT次数表示的时候,状态要转化。
题解here
//============================================================================ // 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; const int INF = 0x3f3f3f3f; struct Trie { int next[510][4],fail[510]; int end[510]; int root,L; int newnode() { for(int i = 0;i < 4;i++) next[L][i] = -1; end[L++] = 0; return L-1; } void init() { L = 0; root = newnode(); } int getch(char ch) { if(ch == 'A')return 0; else if(ch == 'C')return 1; else if(ch == 'G')return 2; else return 3; } void insert(char buf[]) { int len = strlen(buf); int now = root; for(int i = 0;i < len;i++) { if(next[now][getch(buf[i])] == -1) next[now][getch(buf[i])] = newnode(); now = next[now][getch(buf[i])]; } end[now] ++; } void build() { queue<int>Q; fail[root] = root; for(int i = 0;i < 4;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 < 4;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 dp[510][11*11*11*11+5]; int bit[4]; int num[4]; int solve(char buf[]) { int len = strlen(buf); memset(num,0,sizeof(num)); for(int i = 0;i < len;i++) num[getch(buf[i])]++; bit[0] = (num[1]+1)*(num[2]+1)*(num[3]+1); bit[1] = (num[2]+1)*(num[3]+1); bit[2] = (num[3]+1); bit[3] = 1; memset(dp,-1,sizeof(dp)); dp[root][0] = 0; for(int A = 0;A <= num[0];A++) for(int B = 0;B <= num[1];B++) for(int C = 0;C <= num[2];C++) for(int D = 0;D <= num[3];D++) { int s = A*bit[0] + B*bit[1] + C*bit[2] + D*bit[3]; for(int i = 0;i < L;i++) if(dp[i][s] >= 0) { for(int k = 0;k < 4;k++) { if(k == 0 && A == num[0])continue; if(k == 1 && B == num[1])continue; if(k == 2 && C == num[2])continue; if(k == 3 && D == num[3])continue; dp[next[i][k]][s+bit[k]] = max(dp[next[i][k]][s+bit[k]],dp[i][s]+end[next[i][k]]); } } } int ans = 0; int status = num[0]*bit[0] + num[1]*bit[1] + num[2]*bit[2] + num[3]*bit[3]; for(int i = 0;i < L;i++) ans = max(ans,dp[i][status]); return ans; } }; char buf[50]; Trie ac; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int n; int iCase = 0; while( scanf("%d",&n) == 1 && n) { iCase++; ac.init(); for(int i = 0;i < n;i++) { scanf("%s",buf); ac.insert(buf); } ac.build(); scanf("%s",buf); printf("Case %d: %d ",iCase,ac.solve(buf)); } return 0; }
使用最短路预处理出状态的转移。这样可以优化很多
//============================================================================ // 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 INF = 0x3f3f3f3f; struct Trie { int next[60010][2],fail[60010],end[60010]; int root,L; int newnode() { for(int i = 0;i < 2;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]-'0'] == -1) next[now][buf[i]-'0'] = newnode(); now = next[now][buf[i]-'0']; } end[now] = id; } void build() { queue<int>Q; fail[root] = root; for(int i = 0;i < 2;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]] == -1)end[now] = -1; else end[now] |= end[fail[now]]; for(int i = 0;i < 2;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 g[11][11]; int dp[1025][11]; int cnt; int pos[11]; int dis[60010]; void bfs(int k) { queue<int>q; memset(dis,-1,sizeof(dis)); dis[pos[k]] = 0; q.push(pos[k]); while(!q.empty()) { int now = q.front(); q.pop(); for(int i = 0; i< 2;i++) { int tmp = next[now][i]; if(dis[tmp]<0 && end[tmp] >= 0) { dis[tmp] = dis[now] + 1; q.push(tmp); } } } for(int i = 0;i < cnt;i++) g[k][i] = dis[pos[i]]; } int solve(int n) { pos[0] = 0; cnt = 1; for(int i = 0;i < L;i++) if(end[i] > 0) pos[cnt++] = i; for(int i = 0; i < cnt;i++) bfs(i); for(int i = 0;i < (1<<n);i++) for(int j = 0;j < cnt;j++) dp[i][j] = INF; dp[0][0] = 0; for(int i = 0;i <(1<<n);i++) for(int j = 0;j < cnt;j++) if(dp[i][j]<INF) { for(int k = 0;k < cnt;k++) { if(g[j][k] < 0)continue; if( j == k)continue; dp[i|end[pos[k]]][k] = min(dp[i|end[pos[k]]][k],dp[i][j]+g[j][k]); } } int ans = INF; for(int j = 0;j < cnt;j++) ans = min(ans,dp[(1<<n)-1][j]); return ans; } }; Trie ac; char buf[1010]; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int n,m; while(scanf("%d%d",&n,&m) == 2) { if(n == 0 && m == 0)break; ac.init(); for(int i = 0;i < n;i++) { scanf("%s",buf); ac.insert(buf,1<<i); } for(int i = 0;i < m;i++) { scanf("%s",buf); ac.insert(buf,-1); } ac.build(); printf("%d ",ac.solve(n)); } return 0; }
这道题很神,数位DP和AC自动机结合,太强大了。
题解here
//============================================================================ // Name : ZOJ.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; struct Trie { int next[2010][2],fail[2010]; bool end[2010]; int root,L; int newnode() { for(int i = 0;i < 2;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][buf[i]-'0'] == -1) next[now][buf[i]-'0'] = newnode(); now = next[now][buf[i]-'0']; } end[now] = true; } void build() { queue<int>Q; fail[root] = root; for(int i = 0;i < 2;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]])end[now] = true; for(int i = 0;i < 2;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]); } } } }; Trie ac; int bcd[2010][10]; int change(int pre,int num) { if(ac.end[pre])return -1; int cur = pre; for(int i = 3;i >= 0;i--) { if(ac.end[ac.next[cur][(num>>i)&1]])return -1; cur = ac.next[cur][(num>>i)&1]; } return cur; } void pre_init() { for(int i = 0;i <ac.L;i++) for(int j = 0;j <10;j++) bcd[i][j] = change(i,j); } const int MOD = 1000000009; long long dp[210][2010]; int bit[210]; long long dfs(int pos,int s,bool flag,bool z) { if(pos == -1)return 1; if(!flag && dp[pos][s]!=-1)return dp[pos][s]; long long ans = 0; if(z) { ans += dfs(pos-1,s,flag && bit[pos]==0,true); ans %= MOD; } else { if(bcd[s][0]!=-1)ans += dfs(pos-1,bcd[s][0],flag && bit[pos]==0,false); ans %= MOD; } int end = flag?bit[pos]:9; for(int i = 1;i<=end;i++) { if(bcd[s][i]!=-1) { ans += dfs(pos-1,bcd[s][i],flag&&i==end,false); ans %=MOD; } } if(!flag && !z)dp[pos][s] = ans; return ans; } long long calc(char s[]) { int len = strlen(s); for(int i = 0;i < len;i++) bit[i] = s[len-1-i]-'0'; return dfs(len-1,0,1,1); } char str[210]; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; scanf("%d",&T); int n; while(T--) { ac.init(); scanf("%d",&n); for(int i = 0;i < n;i++) { scanf("%s",str); ac.insert(str); } ac.build(); pre_init(); memset(dp,-1,sizeof(dp)); int ans = 0; scanf("%s",str); int len = strlen(str); for(int i = len -1;i >=0;i--) { if(str[i]>'0') { str[i]--; break; } else str[i] = '9'; } ans -= calc(str); ans %=MOD; scanf("%s",str); ans += calc(str); ans %=MOD; if(ans < 0)ans += MOD; printf("%d ",ans); } return 0; }
先简单总结到这吧!抱歉,时间原因,写得很简单,以后有机会好好补充完全吧!