这几天跟着hh大神的博客刷AC自动机,http://www.notonlysuccess.com/index.php/aho-corasick-automaton/
模板题:
Keywords Search 这题不说了,很裸的多模式串的匹配
#include <iostream> #include <cstdio> using namespace std; const int NODE=500010; const int CH=26; int chd[NODE][CH],word[NODE],fail[NODE],sz; void Ins(char *s) { int p=0; for(;*s;s++) { int index=*s-'a'; if(!chd[p][index]) { memset(chd[sz],0,sizeof(chd[sz])); word[sz]=0; chd[p][index]=sz++; } p=chd[p][index]; } word[p]++; } int Que[NODE]; void AC() { int *s=Que,*e=Que; for(int i=0;i<CH;i++) if(chd[0][i]) { *e++=chd[0][i]; fail[chd[0][i]]=0; } while(s!=e) { for(int i=0;i<CH;i++) if(chd[*s][i]) { int v=chd[*s][i]; fail[v]=chd[fail[*s]][i]; *e++=v; } else chd[*s][i]=chd[fail[*s]][i]; s++; } } int solve(char *s) { int sum=0,p=0; for(;*s;s++) { int index=*s-'a'; p=chd[p][index]; for(int tmp=p;tmp>0&&word[tmp]>=0;tmp=fail[tmp]) sum+=word[tmp],word[tmp]=-1; } return sum; } char ss[1000010]; int main() { int ca,cas=1,n; scanf("%d",&ca); while(ca--) { memset(chd[0],0,sizeof(chd[0])); fail[0]=0; sz=1; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%s",ss); Ins(ss); } AC(); scanf("%s",ss); printf("%d\n",solve(ss)); } return 0; }病毒侵袭 这题有多个主串,所以不能把匹配过的word[p]=-1
#include <cstdlib> #include <iostream> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int NODE=101100; #define FF(i,a) for(int i=0;i<a;i++) #define id(c) c int chd[NODE][128],word[NODE],fail[NODE],sz; char s1[500],s2[11000]; vector<int> ans; void Ins(char *s,int val) { int p=0; for(;*s;s++) { if(!chd[p][id(*s)]) { memset(chd[sz],0,sizeof(chd[sz])); word[sz]=0; chd[p][id(*s)]=sz++; } p=chd[p][id(*s)]; } word[p]=val; } int Que[NODE]; void AC() { int *s=Que,*e=Que; FF(i,128) if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } while(s!=e) { int p=*s++; FF(i,128) if(chd[p][i]) { int k=chd[p][i]; fail[k]=chd[fail[p]][i]; *e++=k; } else chd[p][i]=chd[fail[p]][i]; } } int search(char *s) { ans.clear(); int p=0,sum=0; for(;*s;s++) { p=chd[p][*s]; for(int tmp=p;tmp>0;tmp=fail[tmp]) if(word[tmp]) sum++,ans.push_back(word[tmp]); } return sum; } int main(int argc, char *argv[]) { int n,m,tot=0; while(scanf("%d",&n)==1) { getchar(); memset(chd[0],0,sizeof(chd[0])); sz=1; for(int i=0;i<n;i++) { scanf("%s",s1); Ins(s1,i+1); } AC(); scanf("%d",&n); getchar(); FF(i,n) { scanf("%s",s2); int dd=search(s2); if(dd!=0) { tot++; printf("web %d:",i+1); sort(ans.begin(),ans.end()); for(int i=0;i<ans.size();i++) printf(" %d",ans[i]); printf("\n"); } } printf("total: %d\n",tot); } system("PAUSE"); return EXIT_SUCCESS; }
这题比较蛋疼,题意以为是单case,但是一直wa,改下就过了
#include <cstdlib> #include <iostream> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int NODE=500010; #define FF(i,a) for(int i=0;i<a;i++) #define id(c) c-'A' int chd[NODE][26],word[NODE],fail[NODE],sz,ans[1100]; char s1[1100][550],s2[2000100]; void Ins(char *s,int val) { int p=0; for(;*s;s++) { if(!chd[p][id(*s)]) { memset(chd[sz],0,sizeof(chd[sz])); word[sz]=0; chd[p][id(*s)]=sz++; } p=chd[p][id(*s)]; } word[p]=val; } int Que[NODE]; void AC() { int *s=Que,*e=Que; FF(i,26) if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } while(s!=e) { int p=*s++; FF(i,26) if(chd[p][i]) { int k=chd[p][i]; fail[k]=chd[fail[p]][i]; *e++=k; } else chd[p][i]=chd[fail[p]][i]; } } void search(char *s) { int p=0,sum=0; for(;*s;s++) { if(!isupper(*s)) {p=0;continue;} p=chd[p][id(*s)]; for(int tmp=p;tmp>0;tmp=fail[tmp]) if(word[tmp]) ans[word[tmp]]++; } } int main(int argc, char *argv[]) { int n; fail[0]=0; while(~scanf("%d",&n)) { getchar(); memset(chd[0],0,sizeof(chd[0])); memset(ans,0,sizeof(ans)); sz=1; for(int i=0;i<n;i++) { scanf("%s",s1[i]); Ins(s1[i],i+1); } AC(); scanf("%s",s2); search(s2); FF(i,n) if(ans[i+1]) printf("%s: %d\n",s1[i],ans[i+1]); } system("PAUSE"); return EXIT_SUCCESS; }
DNA Sequence http://poj.org/problem?id=2778
这次终于弄的有点明白了,
http://blog.henix.info/blog/poj-2778-aho-corasick-dp.html ,
http://hi.baidu.com/%D2%D5%C1%D6010/blog/item/6db06ccf0a3b440993457e7b.html
它讲的不错,把root节点当做所有可能的安全节点,然后构造状态转移矩阵,最多有101中状态,然后如果当前节点+A(或者T,C,G)的状态pre,能够转移的最长公共前缀的状态cur且cur为安全状态,则map[pre][cur]++;
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define mm(a,what) memset(a,what,sizeof(a)) #define ff(i,a) for(int i=0;i<a;i++) long long map[101][101],map1[101][101],map2[101][101],n; int chd[110][4],fail[110],sz,word[110]; char s[11]; int hash[128]; void Ins(char *s) { int p=0; for(;*s;s++) { if(!chd[p][hash[*s]]) { mm(chd[sz],0); word[sz]=0; chd[p][hash[*s]]=sz++; } p=chd[p][hash[*s]]; } word[p]=1; } int Que[1100]; void ac() { int *s=Que,*e=Que; for(int i=0;i<4;i++) if(chd[0][i]) { *e++=chd[0][i]; fail[chd[0][i]]=0; } while(s!=e) { int p=*s++; for(int i=0;i<4;i++) if(chd[p][i]) { fail[chd[p][i]]=chd[fail[p]][i]; *e++=chd[p][i]; word[chd[p][i]]|=word[fail[chd[p][i]]]; } else chd[p][i]=chd[fail[p]][i]; } } void loop() { for(int i=0;i<=sz;i++) if(!word[i]) { for(int j=0;j<4;j++) if(!word[chd[i][j]]) map[i][chd[i][j]]++; } } void solve(long long s){ int i,j,k; if(s==1) for(i=0;i<sz;i++) for(j=0;j<sz;j++) map1[i][j]=map[i][j]; else { solve(s/2); for(i=0;i<sz;i++) for(j=0;j<sz;j++) for(k=0,map2[i][j]=0;k<sz;k++) { map2[i][j]+=map1[i][k]*map1[k][j]; map2[i][j]%=100000; } if(s/2*2==s) { for(i=0;i<sz;i++) for(j=0;j<sz;j++) map1[i][j]=map2[i][j]; } else { for(i=0;i<sz;i++) for(j=0;j<sz;j++) for(k=0,map1[i][j]=0;k<sz;k++) { map1[i][j]+=map2[i][k]*map[k][j]; map1[i][j]%=100000; } } } } int main() { int i,j,k,m; char c[20]; cin>>m>>n; hash['A']=0; hash['C']=1; hash['T']=2; hash['G']=3; sz=1; mm(chd[0],0); for(i=1;i<=m;i++) { cin>>s; Ins(s); } ac(); //getchar(); loop(); /* cout<<sz<<endl; for(int i=0;i<=sz;i++) { for(int j=0;j<=sz;j++) cout<<map[i][j]<<" "; cout<<endl; }*/ solve(n); long long ans=0; for(i=0;i<sz;i++) { ans+=map1[0][i]; ans%=100000; } cout<<ans<<endl; // system("pause"); return 0; }
考研路茫茫-单词情节 http://acm.hdu.edu.cn/showproblem.php?pid=2243这题我快无语了,不知道怎么就Runtime Error(ACCESS_VIOLATION)了,无语了找了块一天了,以后再找吧,用ac自动机建矩阵肯定对的,以后再检查吧
#include <cstdlib> #include <iostream> using namespace std; #define ff(i,a) for(int i=0;i<a;i++) #define mm(a,what) memset(a,what,sizeof(what)) #define ll unsigned __int64 const int NODE=500; int chd[NODE][26],word[NODE],sz,fail[NODE]; void Ins(char *s) { int p=0; for(;*s;s++) { int id=*s-'a'; if(!chd[p][id]) { mm(chd[sz],0); word[sz]=0; chd[p][id]=sz++; } p=chd[p][id]; } word[p]=1; } int Que[500]; void ac() { int *s=Que,*e=Que; ff(i,26) if(chd[0][i]) *e++=chd[0][i],fail[chd[0][i]]=0; while(s!=e) { int p=*s++; ff(i,26) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[chd[p][i]]][i]; word[chd[p][i]]|=word[chd[fail[chd[p][i]]][i]]; } else chd[p][i]=chd[fail[chd[p][i]]][i]; } } ll a1[100][100],a2[100][100],ans[100][100]; char s[400]; struct Matrix{ ll matrix[100][100]; Matrix operator*(const Matrix & m) { Matrix ans; int i,j,k; for (i = 0;i < sz;i++) for (j =0;j < sz ;j ++) for (ans.matrix[i][j]=0, k = 0;k < sz;k++) { ans.matrix[i][j]+=matrix[i][k]*m.matrix[k][j]; } return ans; } }aa,unit; void build() { ff(i,sz) ff(j,sz) aa.matrix[i][j]=0; ff(i,sz) if(!word[i]) ff(j,26) if(!word[chd[i][j]]) aa.matrix[i][chd[i][j]]++; } Matrix pow(Matrix a,ll n) { Matrix p = a,ans = unit; while(n) { if (n&1) ans = ans*p; p = p*p; n>>=1; } return ans; } Matrix getNum(Matrix a,ll n) { if(n==1) return a; Matrix tmp=getNum(a,n/2),ans; if(n&1) { Matrix tmp1=pow(a,n/2+1); Matrix tmp2=tmp1*tmp; ff(i,sz) ff(j,sz) ans.matrix[i][j]=tmp.matrix[i][j]+tmp1.matrix[i][j]+tmp2.matrix[i][j]; } else { Matrix tmp1=pow(a,n/2); Matrix tmp2=tmp1*tmp; ff(i,sz) ff(j,sz) ans.matrix[i][j]=tmp.matrix[i][j]+tmp2.matrix[i][j]; } return ans; } int main(int argc, char *argv[]) { int n; ll L; memset(unit.matrix,0,sizeof(unit.matrix)); ff(i,100) unit.matrix[i][i]=1; while(cin>>n>>L) { mm(chd[0],0); sz=1; ff(i,n) cin>>s,Ins(s); ac(); build(); Matrix ans=getNum(aa,L); ll ans1=0,ans2=0; ff(i,sz) ans1+=ans.matrix[0][i]; memset(aa.matrix,0,sizeof(aa.matrix)); aa.matrix[0][0]=26; ans=getNum(aa,L); ans2=ans.matrix[0][0]-ans1; if(ans2<0) ans2=ans2+((ll)1<<63)+((ll)1<<63); cout<<ans2<<endl; } // system("PAUSE"); return EXIT_SUCCESS; }
wireless password http://acm.hdu.edu.cn/showproblem.php?pid=2825 基于ac自动机的状态压缩的dp,建trie树的总的节点数为100,那么可以再以这100个节点为状态转移,每个节点记录当前节点所能够覆盖的最多的单词数,应为最多只有10个,所以可以用状态压缩,dp[len][state][last] 表示长度为len的覆盖为state的且最后一个状态为last节点。时间复查度O(25*2^10*100)
#include <cstdlib> #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; #define ff(i,a) for(int i=0;i<a;i++) #define mm(a,what) memset(a,what,sizeof(a)) const int N=27; const int M=12; const int NODE=110; const int MOD=20090717; int dp[N][1<<10][NODE],chd[NODE][26],fail[NODE],word[NODE],sz,n,m,k; int bits[1<<10]; void init() { bits[0]=0; for(int i=1;i<(1<<10);i++) bits[i]=bits[i>>1]+(1&i); } void Ins(char *s,int val) { int p=0; for(;*s;s++) { int id=*s-'a'; if(!chd[p][id]) { mm(chd[sz],0); word[sz]=0; chd[p][id]=sz++; } p=chd[p][id]; } word[p]=1<<val; } int Que[NODE]; void ac() { int *s=Que,*e=Que; ff(i,26) if(chd[0][i]) { *e++=chd[0][i]; fail[chd[0][i]]=0; } while(s!=e) { int p=*s++; ff(i,26) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[p]][i]; word[chd[p][i]]|=word[fail[chd[p][i]]]; } else chd[p][i]=chd[fail[p]][i]; } } void solve() { memset(dp,0,sizeof(dp)); dp[0][0][0]=1; int full=1<<m; for(int i=0;i<=n;i++) for(int j=0;j<full;j++) for(int d=0;d<sz;d++) { if(!dp[i][j][d]) continue; ff(d1,26) { int state=word[chd[d][d1]]; dp[i+1][state|j][chd[d][d1]]=(dp[i+1][state|j][chd[d][d1]]+dp[i][j][d])%MOD; } } int ans=0; // ff(i,n+1) cout<<dp[i][0][0]<<endl; for(int i=0;i<(1<<m);i++) if(bits[i]>=k) for(int j=0;j<sz;j++) ans+=dp[n][i][j],ans%=MOD; printf("%d\n",ans); } char s[20]; int main(int argc, char *argv[]) { init(); while(scanf("%d %d %d",&n,&m,&k)==3) { getchar(); if(!n&&!m&&!k) break; mm(chd[0],0); sz=1; ff(i,m) { scanf("%s",s); Ins(s,i); } ac(); solve(); } //system("PAUSE"); return EXIT_SUCCESS; }
http://acm.uestc.edu.cn/problem.php?pid=1633
给出最多为20个模式串,每个串长最多为15,然后用最多1000个字符包含最多多少个模式串。dp[len][state]表示在state节点结尾的长度为len的个数
#include <cstdlib> #include <iostream> #include <cstring> #include <cstdio> using namespace std; #define ff(i,a) for(int i=0;i<a;i++) #define mm(a,what) memset(a,what,sizeof(a)) const int NODE=410; const int Maxlen=1100; int chd[NODE][3],word[NODE],fail[NODE],sz; long long dp[Maxlen][NODE]; int n,m; void Ins(char *s,int v) { int p=0; for(;*s;s++) { int id=*s-'A'; if(!chd[p][id]) { mm(chd[sz],0); word[sz]=0; chd[p][id]=sz++; } p=chd[p][id]; } word[p]+=v; } int Que[NODE]; void ac() { int *s=Que,*e=Que; ff(i,3) if(chd[0][i]) { *e++=chd[0][i]; fail[chd[0][i]]=0; } while(s!=e) { int p=*s++; ff(i,3) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[p]][i]; word[chd[p][i]]+=word[fail[chd[p][i]]]; } else chd[p][i]=chd[fail[p]][i]; } } void solve() { mm(dp,-1); dp[0][0]=0; long long Max=0; ff(i,n+1)ff(j,sz) { if(dp[i][j]==-1) continue; ff(k,3) { int next=chd[j][k]; if(dp[i+1][next]<dp[i][j]+word[next]) { dp[i+1][next]=dp[i][j]+word[next]; } } if(dp[i][j]>Max) Max=dp[i][j]; } printf("%lld\n",Max); } char s[110][20]; int val[110]; int main(int argc, char *argv[]) { while(scanf("%d %d",&m,&n)==2) { getchar(); mm(chd[0],0); sz=1; ff(i,m) scanf("%s",s[i]); ff(i,m) Ins(s[i],1); ac(); solve(); } //system("PAUSE"); return EXIT_SUCCESS; }
Ring http://acm.hdu.edu.cn/showproblem.php?pid=2296
这题输入输出太蛋疼了,卡了我两天的时间,无语了
#include <cstdlib> #include <iostream> #include <String> #include <cstring> using namespace std; #define ff(i,a) for(int i=0;i<a;i++) #define mm(a,what) memset(a,what,sizeof(a)) const int NODE=1100; const int Maxlen=55; int chd[NODE][26],fail[NODE],sz; __int64 word[NODE]; __int64 dp[Maxlen][NODE]; int n,m; struct Node { string s; }node[Maxlen][NODE]; void Ins(char *s,__int64 v) { int p=0; for(;*s;s++) { int id=*s-'a'; if(!chd[p][id]) { mm(chd[sz],0); word[sz]=0; chd[p][id]=sz++; } p=chd[p][id]; } word[p]=v; } int Que[NODE]; void ac() { int *s=Que,*e=Que; ff(i,26) if(chd[0][i]) { *e++=chd[0][i]; fail[chd[0][i]]=0; } while(s!=e) { int p=*s++; ff(i,26) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[p]][i]; word[chd[p][i]]+=word[fail[chd[p][i]]]; } else chd[p][i]=chd[fail[p]][i]; } } void solve() { mm(dp,-1); dp[0][0]=0; node[0][0].s=""; string ans=""; __int64 Max=0; ff(i,n+1)ff(j,sz) { if(dp[i][j]==-1) continue; ff(k,26) { int next=chd[j][k]; if(dp[i+1][next]<dp[i][j]+word[next]) { dp[i+1][next]=dp[i][j]+(__int64)word[next]; node[i+1][next].s=node[i][j].s+(char)(k+'a'); } else if(dp[i+1][next]==dp[i][j]+word[next]) { string ss=node[i][j].s+(char)(k+'a'); if(node[i+1][next].s.length()>ss.length()) node[i+1][next].s=ss; else if(node[i+1][next].s.length()==ss.length()&&node[i+1][next].s>ss) node[i+1][next].s=ss; } } if(dp[i][j]>Max) Max=dp[i][j],ans=node[i][j].s; else if(dp[i][j]==Max) { if(ans.length()>node[i][j].s.length()) ans=node[i][j].s; else if(ans.length()==node[i][j].s.length()&&ans>node[i][j].s) ans=node[i][j].s; } } cout<<ans<<endl; } char s[1100][150]; __int64 val[110]; int main(int argc, char *argv[]) { int ca; scanf("%d",&ca); while(ca--) { scanf("%d %d",&n,&m); getchar(); mm(chd[0],0); sz=1; ff(i,m) scanf("%s",s[i]); ff(i,m) scanf("%I64d",&val[i]); ff(i,m) Ins(s[i],val[i]); ac(); solve(); } // system("PAUSE"); return EXIT_SUCCESS; }
DNA repair http://acm.hdu.edu.cn/showproblem.php?pid=2457 这题很简单,跟上一题状态表示一样,dp[len][state]表示长度len到达state状态所要修改的最小值,不能走到尾节点,太坑了这题,半个小时敲完代码,结果一个小bug调试了我两个小时
#include <cstdlib> #include <iostream> #include <cstring> #include <cstdio> using namespace std; #define mm(a,what) memset(a,what,sizeof(a)) #define ff(i,a) for(int i=0;i<a;i++) const int NODE=1010; const int maxlen=1010; const int ch=4; const int INF=99999999; int chd[NODE][ch],word[NODE],fail[NODE],sz; int sw[128],dp[maxlen][NODE]; char ss[maxlen],ss1[100]; void Ins(char *s1) { int p=0; for(;*s1;s1++) { int id=sw[*s1]; if(!chd[p][id]) { mm(chd[sz],0); word[sz]=0; chd[p][id]=sz++; } p=chd[p][id]; } word[p]=1; } int Que[NODE]; void ac() { int *s=Que,*e=Que; ff(i,ch) if(chd[0][i]) { *e++=chd[0][i]; fail[chd[0][i]]=0; } while(s!=e) { int p=*s++; ff(i,ch) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[p]][i]; word[chd[p][i]]|=word[fail[chd[p][i]]]; } else chd[p][i]=chd[fail[p]][i]; } } void solve() { int len=strlen(ss); ff(i,len+1) ff(j,sz) dp[i][j]=INF; dp[0][0]=0; int ans=INF; ff(i,len+1) ff(j,sz) { if(dp[i][j]==INF) continue; ff(k,ch) if(!word[chd[j][k]]) { int next=chd[j][k],tmp; if(sw[ss[i]]==k) tmp=dp[i][j]; else tmp=dp[i][j]+1; dp[i+1][next]=min(dp[i+1][next],tmp); } } ff(i,sz) ans=min(ans,dp[len][i]); if(ans!=INF) printf("%d\n",ans); else printf("-1\n"); } int main(int argc, char *argv[]) { sw['A']=0; sw['T']=1; sw['C']=2; sw['G']=3; int n,ca=1; while(scanf("%d",&n)==1&&n) { sz=1; mm(chd[0],0); // getchar(); ff(i,n) { scanf("%s",ss1); Ins(ss1); } ac(); scanf("%s",ss); printf("Case %d: ",ca++); solve(); } //system("PAUSE"); return EXIT_SUCCESS; }
Searching the string http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3441
这个应该不算是dp吧,应该说的是贪心,建立ac自动机,如果当前匹配的单词和上次有重复的话就舍弃
#include <cstdlib> #include <iostream> #include <cstring> #include <cstdio> using namespace std; #define ff(i,a) for(int i=0;i<a;i++) #define mm(a,what) memset(a,what,sizeof(a)) const int NODE=1000010; const int ch=26; int chd[600010][ch],word[NODE],fail[NODE],sz; char txt[NODE],ss[NODE]; int ans[NODE][2],last[NODE],len[NODE],pos[100010],n,id[100010]; int flag[NODE]; int Ins(char *s) { int p=0; for(;*s;s++) { int id=*s-'a'; if(!chd[p][id]) { mm(chd[sz],0); word[sz]=0; flag[sz]=0; ans[sz][0]=ans[sz][1]=0; last[sz]=-1; chd[p][id]=sz++; } p=chd[p][id]; } word[p]=1; flag[p]=1; len[p]=strlen(ss); return p; } int Que[NODE]; void ac() { int *s=Que,*e=Que; ff(i,26) if(chd[0][i]) { *e++=chd[0][i]; fail[chd[0][i]]=0; } while(s!=e) { int p=*s++; ff(i,26) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[p]][i]; flag[chd[p][i]]|=flag[fail[chd[p][i]]]; } else chd[p][i]=chd[fail[p]][i];; } } void search() { int len1=strlen(txt),p=0; ff(i,len1) { p=chd[p][txt[i]-'a']; for(int tmp=p;tmp&&flag[tmp];tmp=fail[tmp]) if(word[tmp]) { ans[tmp][0]++; if((i-last[tmp])>= len[tmp]) ans[tmp][1]++,last[tmp]=i; } } } int main(int argc, char *argv[]) { int ca=1; while(scanf("%s",txt)==1) { getchar(); scanf("%d",&n); mm(chd[0],0); sz=1; ff(i,n) { scanf("%d %s",&id[i],ss); pos[i]=Ins(ss); } ac(); search(); // ff(i,sz) cout<<ans[i][0]<<" "<<ans[i][1]<<endl; printf("Case %d\n",ca++); ff(i,n) printf("%d\n",ans[pos[i]][id[i]]); puts(""); } //system("PAUSE"); return EXIT_SUCCESS; }
Lost's revenge http://acm.hdu.edu.cn/showproblem.php?pid=3341
这题不愧为神题呀,要非常优化的代码才能过,首先主串最多长40,正常用dp可以dp[40][40][40][40][state]来表示用了A T C G 以状态state结尾的最多的个数,这样肯定超内存,用状态压缩dp[dd][state] ,dd为压缩的状态,他有4位,每位的权值为bits[0] bits[1] bits[2] bits[3],这样时间复查度 O(10^4*500*4)
#include <cstdlib> #include <iostream> #include <cstring> #include <cstdio> using namespace std; #define ff(i,a) for(int i=0;i<a;i++) #define mm(a,what) memset(a,what,sizeof(a)) const int NODE=550; int chd[NODE][4],fail[NODE],word[NODE],sz,sw[128]; int lim[4],Limit,n,ans; void Ins(char *s) { int p=0; for(;*s;s++) { int id=sw[*s]; if(!chd[p][id]) { mm(chd[sz],0); word[sz]=0; chd[p][id]=sz++; } p=chd[p][id]; } word[p]++; } int Que[NODE]; void ac() { int *s=Que,*e=Que; ff(i,4) if(chd[0][i]) { *e++=chd[0][i]; fail[chd[0][i]]=0; } while(s!=e) { int p=*s++; ff(i,4) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[p]][i]; word[chd[p][i]]+=word[fail[chd[p][i]]]; } else chd[p][i]=chd[fail[p]][i]; } } int dp[29999][550],bits[4]; void solve() { mm(dp,-1); dp[0][0]=0; ff(i,lim[0]+1) ff(j,lim[1]+1) ff(k,lim[2]+1) ff(r,lim[3]+1) { int base=i+j*bits[1]+k*bits[2]+r*bits[3]; ff(s1,sz) { if(dp[base][s1]==-1) continue; if(i!=lim[0]) { dp[base+1][chd[s1][0]]=max(dp[base+1][chd[s1][0]],dp[base][s1]+word[chd[s1][0]]); } if(j!=lim[1]) { int next=base+bits[1]; dp[next][chd[s1][1]]=max(dp[next][chd[s1][1]],dp[base][s1]+word[chd[s1][1]]); } if(k!=lim[2]) { int next=base+bits[2]; dp[next][chd[s1][2]]=max( dp[next][chd[s1][2]],dp[base][s1]+word[chd[s1][2]]); } if(r!=lim[3]) { int next=base+bits[3]; dp[next][chd[s1][3]]=max(dp[next][chd[s1][3]],dp[base][s1]+word[chd[s1][3]]); } // ans=max(ans,dp[base][s1]); } } for(int i=0;i<sz;i++) ans=max(ans,dp[lim[0]*bits[0]+lim[1]*bits[1]+lim[2]*bits[2]+lim[3]*bits[3]][i]); } char ss[55]; int main(int argc, char *argv[]) { sw['A']=0; sw['T']=1; sw['C']=2; sw['G']=3; int ca=1; while(scanf("%d",&n)==1&&n) { fail[0]=0; mm(chd[0],0); sz=1; ff(i,n) scanf("%s",ss),Ins(ss); ac(); scanf("%s",ss); Limit=strlen(ss); mm(lim,0); ff(i,Limit) lim[sw[ss[i]]]++; bits[0]=1; bits[1]=(lim[0]+1); bits[2]=(lim[0]+1)*(lim[1]+1); bits[3]=(lim[0]+1)*(lim[1]+1)*(lim[2]+1); ans=0; solve(); printf("Case %d: %d\n",ca++,ans); } // system("PAUSE"); return EXIT_SUCCESS; }
空罐 Cans http://zerojudge.tw/ShowProblem?problemid=b179
这题NB呀,做完这题后高中生在我心中的形象瞬间高大了,这么好的题!感谢hh神牛提供思路,不然这题我真想不出来,dp[len][state] 表示长度为len并且尾节点在state处的
个数,用fail指针用作去掉头节点作为状态转移,chd[][]用作从后加一个字符,用滚动数组优化
我们因为是主要关注的是一个串的末尾的情况,当我们要在开头删掉一个串时,如果这个串串长比当前根节点到改点的距离要大的话,删掉串的首字母其实是该节点是不移动的,否则的话该节点要跟着fail指针走,fail指针节点的高度肯定要比该节点高度要小,所以这样就至少删掉了一个字母
考虑增长的情况,在末尾添加一个字符,可能会使他生病,否则的话跟着chd走就行了
#include <cstdlib> #include <iostream> #include <cstdio> #include <cstring> using namespace std; #define ff(i,a) for(int i=0;i<a;i++) #define mm(a,what) memset(a,what,sizeof(a)) const int NODE=1510; const int maxlen=110; const int MODE=10007; int chd[NODE][4],fail[NODE],word[NODE],sz,dp[2][maxlen][NODE],n,P; int dis[NODE]; char native[110],ss[110]; void Ins() { int p=0,len=strlen(ss); for(int i=0;i<len;i++) { int id=ss[i]-'a'; if(!chd[p][id]) { mm(chd[sz],0); word[sz]=0; chd[p][id]=sz++; } p=chd[p][id]; dis[p]=i+1; } word[p]=1; } int Que[NODE]; void ac() { int *s=Que,*e=Que; ff(i,4) if(chd[0][i]) { *e++=chd[0][i]; fail[chd[0][i]]=0; } while(s!=e) { int p=*s++; ff(i,4) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[p]][i]; word[chd[p][i]]|=word[fail[chd[p][i]]]; } else chd[p][i]=chd[fail[p]][i]; } } void solve() { int ans1=0,ans2=0,p=0,len=strlen(native); for(int i=0;native[i];i++) p=chd[p][native[i]-'a']; if(word[p]) { printf("0 1\n");return; } mm(dp,0); dp[0][len][p]=1; int Maxlen=len,now=0; while(P--) { mm(dp[now^1],0); for(int i=1;i<=Maxlen;i++) for(int j=0;j<sz;j++) { if(!dp[now][i][j]) continue; if(i==1) ans1=(ans1+dp[now][i][j])%MODE; else if(i<=dis[j]) dp[now^1][i-1][fail[j]]=(dp[now^1][i-1][fail[j]]+dp[now][i][j])%MODE; else dp[now^1][i-1][j]=(dp[now^1][i-1][j]+dp[now][i][j])%MODE; } for(int i=1;i<=Maxlen;i++) for(int j=0;j<sz;j++) for(int k=0;k<4;k++) if(!word[chd[j][k]]) dp[now^1][i+1][chd[j][k]]=(dp[now^1][i+1][chd[j][k]]+dp[now][i][j])%MODE; else ans2=(ans2+dp[now][i][j])%MODE; now=1-now; Maxlen++; } printf("%d %d\n",ans1,ans2); } int main(int argc, char *argv[]) { while(scanf("%s",native)==1) { scanf("%d %d",&P,&n); mm(chd[0],0); sz=1; dis[0]=0; ff(i,n) scanf("%s",ss),Ins(); ac(); solve(); } //system("PAUSE"); return EXIT_SUCCESS; }
Resource Archiver http://acm.hdu.edu.cn/showproblem.php?pid=3247
最后一题了,窃喜以下,这题开始把memset(a,what,sizeof(a))写成了 memset(a,what,sizeof(waht)) re了一个上午,之前都犯过这样的错,白痴呀!然后MLE,然后TLE,各种蛋疼……题意是给出最多10个串Resource,每个串长最多为1000,在给出最多1000个病毒串,最长为50000,求一个串包含所有的Resource但是不含任何病毒码的最小长度;
空间优化:因为Resource的串长最多为1000,所以长度超过1000个病毒串不可能污染Resource,那么就不考虑这个病毒串,然后建立ac自动机空间复查度由O(50000*1000*2)优化到O(1000*1000*2)
时间优化:在ac自动机上求Resource之间的最短路 dp[state][end] 这样 0<=end<10 ,缩短了状态数量 由O(2^10*10*10)
很难看的代码
#include <cstdlib> #include <iostream> #include <cstdio> using namespace std; #define ff(i,a) for(int i=0;i<a;i++) #define mm(a,what) memset(a,what,sizeof(a)) const int NODE=1000010; int chd[NODE][2],word[NODE],fail[NODE],sz; int dp[1<<10][11]; int n,m,limit; char ss[1010],ss1[50110]; int Ins(char *s,int val) { int p=0; for(;*s;s++) { int id=*s-'0'; if(!chd[p][id]) { mm(chd[sz],0); word[sz]=0; chd[p][id]=sz++; } p=chd[p][id]; } if(val==-1) word[p]=val; else word[p]=1<<val; return p; } int Que[NODE]; bool vis[NODE]; int dis[NODE]; int end[11],height[11]; void ac() { int *s=Que,*e=Que; ff(i,2) if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } while(s!=e) { int p=*s++; ff(i,2) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[p]][i]; if(word[fail[chd[p][i]]]==-1) word[chd[p][i]]=-1; else if(word[chd[p][i]]!=-1) word[chd[p][i]]|=word[fail[chd[p][i]]]; } else chd[p][i]=chd[fail[p]][i]; } } void find_path(int st) { int *s=Que,*e=Que; *e++=st; mm(vis,0); mm(dis,-1); if(word[st]==-1) return; vis[st]=1; dis[st]=0; while(s!=e) { int p=*s++; ff(i,2) if(!vis[chd[p][i]]) { vis[chd[p][i]]=1; if(word[chd[p][i]]!=-1) dis[chd[p][i]]=dis[p]+1,*e++=chd[p][i]; } } } int g[11][11]; int min1(int a,int b) { if(a==-1) return b; return a<b?a:b; } void solve() { int now=0,ans=1,Max=1<<n; bool find=0; ff(i,n) { find_path(end[i]); ff(j,n) g[i][j]=dis[end[j]]; } mm(dp,-1); ff(i,n) if(word[end[i]]!=-1) dp[word[end[i]]][i]=height[i]; ff(i,Max) ff(j,n) if(dp[i][j]!=-1) { ff(k,n) if((i&(1<<k))==0&&g[i][j]!=-1) dp[i|(1<<k)][k]=min1(dp[i|(1<<k)][k],dp[i][j]+g[j][k]); } ans=-1; ff(i,n) if(dp[Max-1][i]!=-1) ans=min1(ans,dp[Max-1][i]); printf("%d\n",ans); } int main(int argc, char *argv[]) { int Maxlen; while(scanf("%d %d",&n,&m)==2&&(n||m)) { getchar(); mm(chd[0],0); sz=1; Maxlen=0; ff(i,n) { scanf("%s",ss),end[i]=Ins(ss,i); height[i]=strlen(ss); int len=strlen(ss); Maxlen=max(Maxlen,len); } limit=sz; ff(i,m) { scanf("%s",ss1); if(strlen(ss1)<=Maxlen) Ins(ss1,-1); } ac(); solve(); } //system("PAUSE"); return EXIT_SUCCESS; }