首页 > 代码库 > 每日总结-05-19(AC自动机结束)
每日总结-05-19(AC自动机结束)
今天下午讨论了一下校赛的题,终于最终拍板,把校赛的题目定下来了。
然后今天A掉了4个AC自动机的题目。终于完成了AC自动机专辑里面的15个题。至此AC自动机完全结束。
明天开启线段树专题。。。。。
------------------------------------------------------------------------------------------------------------------------------------
1,,hdu-2457-DNA repair
题目:给出一些不合法的模式DNA串,给出一个原串,问最少需要修改多少个字符,使得原串中不包含非法串
做法:把病毒串做成一个trie树。
DP[i][j]:长度为i,在trie树上的状态为j,需要改变的最少的字符的个数。
dp[i][j]=dp[i-1][k]+转移费用。
#include<stdio.h> #include<algorithm> #include<iostream> #include<stdlib.h> #include<vector> #include<queue> #include<string.h> #include<math.h> using namespace std; #define LL __int64 #define maxn 55 const int maxnode=55*21; const int childnum=5; const int mod=1000000007; const int INF=99999999; struct ac_tree { int chd[maxnode][childnum]; int val[maxnode]; int fail[maxnode]; int key[1<<11]; int Q[maxnode]; int ID[128]; int sz; int dp[1100][maxnode]; void init() { fail[0]=0; for(int i=0;i<childnum;i++) { ID[i]=i; } ID[‘A‘]=1;ID[‘G‘]=2; ID[‘C‘]=3;ID[‘T‘]=4; } void reset() { memset(chd,0,sizeof(chd)); sz=1; } void insert(char str[],int k) { int p=0; int len=strlen(str); for(int i=0;i<len;i++) { int c=ID[str[i]]; if(!chd[p][c]) { memset(chd[sz],0,sizeof(chd[sz])); val[sz]=0; chd[p][c]=sz++; } p=chd[p][c]; } val[p]=k; } void ac_build() { int *s=Q,*e=Q; for(int i=1;i<childnum;i++) { if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } } while(s!=e) { int u=*s++; if(val[fail[u]])val[u]+=val[fail[u]]; for(int i=1;i<childnum;i++) { int &v=chd[u][i]; if(v) { *e++=v; fail[v]=chd[fail[u]][i]; // val[v]=(val[v]+val[fail[v]]); } else v=chd[fail[u]][i]; } } } int work(char str[]) { int len=strlen(str); int i,j,k; for(i=0;i<=len;i++) for(j=0;j<sz;j++)dp[i][j]=INF; dp[0][0]=0; int cr,x; for(i=0;i<len;i++) { for(j=0;j<sz;j++) { if(dp[i][j]==INF)continue; for(k=1;k<childnum;k++) { cr=chd[j][k]; if(val[cr])continue; x=dp[i][j]; if(k!=ID[str[i]])x++; dp[i+1][cr]=min(dp[i+1][cr],x); } } } int maxx=INF; for(j=0;j<sz;j++) { maxx=min(maxx,dp[len][j]); } if(maxx==INF)cout<<"-1"<<endl; else cout<<maxx<<endl; } }AC; char tmp[1550]; int main() { AC.init(); int n,m,k,x; int T; T=0; while(~scanf("%d",&n)&&(n)) { T++; AC.reset(); for(int i=1;i<=n;i++) { scanf("%s",tmp); AC.insert(tmp,1); } AC.ac_build(); scanf("%s",tmp); printf("Case %d: ",T); AC.work(tmp); } return 0; }
2,zoj-3228-Searching the String
题目大意:给出一个字符串和若干个单词,问这些单词在字符串里面出现了多少次。单词前面为0表示这个单词可重叠出现,1为不可重叠出现。
做法: 可重叠的很好处理,就是AC自动机处理。不可重叠的就记录下上次这个单词出现的位置,如果上次出现的位置加上这个单词的长度小于等于当前位置的话,那么这个单词的次数就加一。否则就不加。
注意:可能存在两个字符串相等,比较坑爹。。。
#include<stdio.h> #include<algorithm> #include<iostream> #include<stdlib.h> #include<vector> #include<queue> #include<string.h> #include<math.h> using namespace std; #define LL __int64 #define maxn 55 const int maxnode=6*110000; const int childnum=27; const int mod=1000000007; const int INF=99999999; struct ac_tree { int chd[maxnode][childnum]; int val[maxnode][2]; int fail[maxnode]; int last[maxnode]; int num[maxnode][2]; int *ans[maxnode]; int key[1<<11]; int Q[maxnode]; int ID[128]; int sz; void init() { fail[0]=0; for(int i=0;i<childnum;i++) { ID[i+‘a‘]=i+1; } } void reset() { memset(chd,0,sizeof(chd)); memset(num,0,sizeof(num)); sz=1; } void insert(char str[],int k,int tt) { int p=0; int len=strlen(str); for(int i=0;i<len;i++) { int c=ID[str[i]]; if(!chd[p][c]) { memset(chd[sz],0,sizeof(chd[sz])); val[sz][tt]=0; chd[p][c]=sz++; } p=chd[p][c]; } val[p][tt]=len; last[p]=-1; ans[k]=&num[p][tt]; } void ac_build() { int *s=Q,*e=Q; for(int i=1;i<childnum;i++) { if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } } while(s!=e) { int u=*s++; //if(val[fail[u]])val[u]+=val[fail[u]]; for(int i=1;i<childnum;i++) { int &v=chd[u][i]; if(v) { *e++=v; fail[v]=chd[fail[u]][i]; // val[v]=(val[v]+val[fail[v]]); } else v=chd[fail[u]][i]; } } } int work(char str[],int n) { int len=strlen(str); int root=0,key,tp,x; for(int i=0;i<len;i++) { tp=ID[str[i]]; root=chd[root][tp]; key=root; while(key!=0) { if(val[key][0]!=0)num[key][0]++; if(val[key][1]!=0) { if(last[key]<=i-val[key][1]) { num[key][1]++; last[key]=i; } } key=fail[key]; } } for(int i=1;i<=n;i++) printf("%d\n",*ans[i]); puts(""); } }AC; char tmp[1550]; char as[110000]; int main() { AC.init(); int n,m,k,x; int T; T=0; while(~scanf("%s",as)) { T++; AC.reset(); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d %s",&k,tmp); AC.insert(tmp,i,k); } AC.ac_build(); printf("Case %d\n",T); AC.work(as,n); } return 0; }
3,hdu-3341-Lost‘s revenge
抄的解题报告,感觉说的很详细,我就不赘余了。。
题意:给出一些病毒串,一个原串,问怎么调整原串的顺序使得跟最多的病毒串匹配
抄解题报告:
自动机+DP。设主串有na个A、nc个C、ng个G、nt个T,于是题意转化为用na个A、
nc个C、ng个G、nt个T生成一个新串,使模式串匹配次数最大。先将模式串生成自动机
〔trie图〕,然后在这上面进行DP,状态可表示为dp[d,s],d为自动机状态,
s表示由ma个A、mc个C、mg个G、mt个T的生成串
〔s可表示为ma*(nc+1)*(ng+1)*(nt+1)+mc*(ng+1)*(nt+1)+mg*(nt+1)+mt〕,
转移为O(4),总复杂度O(500*11^4*4)
那种表示方法就是变进制吧
直接DP(root,s)会超时
Qinz说这题卡时紧
我用他的那种dfs才能过,他是先dfs枚举出状态,然后对这个状态递推计算,好快
比较神奇,不用管顺序的,就记录个数
#include<stdio.h> #include<algorithm> #include<iostream> #include<stdlib.h> #include<vector> #include<queue> #include<string.h> #include<math.h> using namespace std; #define LL __int64 #define maxn 55 const int maxnode=51*10; const int childnum=5; const int mod=1000000007; const int INF=99999999; struct ac_tree { int chd[maxnode][childnum]; int val[maxnode]; int fail[maxnode]; int Q[maxnode]; int ID[128]; int sz; int dp[maxnode][14642]; void init() { fail[0]=0; for(int i=0;i<childnum;i++) { ID[i+‘a‘]=i+1; } ID[‘A‘]=1;ID[‘T‘]=2; ID[‘G‘]=3;ID[‘C‘]=4; } void reset() { memset(chd,0,sizeof(chd)); memset(val,0,sizeof(val)); sz=1; } void insert(char str[],int k) { // cout<<str<<endl; int p=0; int len=strlen(str); for(int i=0;i<len;i++) { int c=ID[str[i]]; if(!chd[p][c]) { memset(chd[sz],0,sizeof(chd[sz])); val[sz]=0; chd[p][c]=sz++; } p=chd[p][c]; } val[p]+=k; //cout<<p<<" "<<val[p]<<endl; } void ac_build() { int *s=Q,*e=Q; for(int i=1;i<childnum;i++) { if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } } while(s!=e) { int u=*s++; if(val[fail[u]])val[u]+=val[fail[u]]; for(int i=1;i<childnum;i++) { int &v=chd[u][i]; if(v) { *e++=v; fail[v]=chd[fail[u]][i]; // val[v]=(val[v]+val[fail[v]]); } else v=chd[fail[u]][i]; } } } int work(char str[]) { int num[5]; memset(num,0,sizeof(num)); int len=strlen(str); for(int i=0;i<len;i++)num[ID[str[i]]]++; dp[0][0]=0; int tai,cr; int sum[6]; sum[5]=1; sum[4]=num[4]+1; sum[3]=(num[3]+1)*sum[4]; sum[2]=(num[2]+1)*sum[3]; sum[1]=(num[1]+1)*sum[2]; int a[5],i,j; int pt; memset(dp,-1,sizeof(dp)); dp[0][0]=0; for(a[1]=0;a[1]<=num[1];a[1]++) for(a[2]=0;a[2]<=num[2];a[2]++) for(a[3]=0;a[3]<=num[3];a[3]++) for(a[4]=0;a[4]<=num[4];a[4]++) { if(a[1]+a[2]+a[3]+a[4]>=len)break; tai=0; for(i=1;i<=4;i++)tai+=a[i]*sum[i+1]; for(i=0;i<sz;i++) { if(dp[i][tai]==-1)continue; for(j=1;j<childnum;j++) { if(a[j]+1>num[j])continue; cr=chd[i][j]; pt=tai+sum[j+1]; // if(dp[cr][pt]==-1)dp[cr][pt]=dp[i][tai]; dp[cr][pt]=max(dp[cr][pt],dp[i][tai]+val[cr]); // cout<<i<<" "<<j<<"->"<<cr<<" "<<pt<<" "<<dp[cr][pt]<<endl; } } } pt=num[1]*sum[2]+num[2]*sum[3]+num[3]*sum[4]+num[4]; int maxx=-1; for(i=0;i<sz;i++) { maxx=max(maxx,dp[i][pt]); } cout<<maxx<<endl; } }AC; char tmp[1550]; char as[110000]; int main() { AC.init(); int n,m,k,x; int T; T=0; while(~scanf("%d",&n)&&n) { T++; AC.reset(); for(int i=1;i<=n;i++) { scanf("%s",tmp); AC.insert(tmp,1); } AC.ac_build(); scanf("%s",as); printf("Case %d: ",T); AC.work(as); } return 0; }
4,hdu-3247-Resource Archiver
题意:
给你 n 个串,和 m 个病毒串。要把这 n 个串
连起来来,可以重叠,但不能包含 m 个病毒串
中的任意一个。求把 n 个串连起来的最小长度。
做法:这个题目憋了我很长时间,一直不知道怎么样处理。后来经过翻阅博客,终于悟出来点。
把病毒串和非病毒串都标记在trie树上处理。然后病毒串的地方标记为病毒串,非病毒串的地方标记为是哪一个串。状态压缩一下。
然后从每个非病毒串的结尾开始bfs。
map[i][j]:从i字符串的结尾,到j字符串的结尾的最短路径是多少。
然后就可以DP了。
dp[i][j]:已用的字符串为i状态,在trie树上的点为j状态,的最短路径。
#include<stdio.h> #include<algorithm> #include<iostream> #include<stdlib.h> #include<vector> #include<queue> #include<string.h> #include<math.h> using namespace std; #define LL __int64 #define maxn 205 const int maxnode=100001; const int childnum=3; const int mod=1000000007; const int INF=99999999; struct list { int st; int x; }pp,qq; struct ac_tree { int chd[maxnode][childnum]; int val[maxnode]; int fail[maxnode]; int Q[maxnode]; int var[maxnode]; int vis[maxnode]; int dist[maxnode]; int st[maxn]; int ID[128]; int sz; int maps[maxn][maxn]; int ns; int dp[1<<11][maxn]; void init() { fail[0]=0; for(int i=0;i<childnum;i++) { ID[i+‘a‘]=i+1; } ID[‘0‘]=1;ID[‘1‘]=2; } void reset() { memset(chd,0,sizeof(chd)); memset(val,0,sizeof(val)); memset(var,0,sizeof(var)); memset(maps,-1,sizeof(maps)); sz=1; } void insert(char str[],int k,int ps) { // cout<<str<<endl; int p=0; int len=strlen(str); for(int i=0;i<len;i++) { int c=ID[str[i]]; if(!chd[p][c]) { memset(chd[sz],0,sizeof(chd[sz])); val[sz]=0; chd[p][c]=sz++; } p=chd[p][c]; } if(ps==0) { val[p]=k; var[p]=0; } else var[p]=1; //cout<<p<<" "<<val[p]<<endl; } void ac_build() { int *s=Q,*e=Q; for(int i=1;i<childnum;i++) { if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } } while(s!=e) { int u=*s++; //if(val[fail[u]]==-1)val[u]=-1; for(int i=1;i<childnum;i++) { int &v=chd[u][i]; if(v) { *e++=v; fail[v]=chd[fail[u]][i]; val[v]=(val[v]|val[fail[v]]); var[v]=(var[v]|var[fail[v]]); } else v=chd[fail[u]][i]; } } } int bfs(int u) { queue<int>que; while(!que.empty())que.pop(); int x=st[u]; que.push(x); memset(dist,-1,sizeof(dist)); dist[x]=0; while(!que.empty()) { x=que.front(); que.pop(); for(int i=1;i<childnum;i++) { int y=chd[x][i]; if(var[y])continue; if(dist[y]<0) { dist[y]=dist[x]+1; que.push(y); } } } for(int i=0;i<ns;i++) { maps[u][i]=dist[st[i]]; //cout<<u<<"->"<<i<<"="<<maps[u][i]<<endl; } } int work(int n) { st[0]=0; ns=1; for(int i=0;i<sz;i++) if(val[i]) { st[ns++]=i; //cout<<ns-1<<" "<<val[st[ns-1]]<<endl; } for(int i=0;i<ns;i++)bfs(i); memset(dp,-1,sizeof(dp)); dp[0][0]=0; int cr; for(int i=0;i<(1<<n);i++) { for(int j=0;j<ns;j++) { if(dp[i][j]<0)continue; for(int k=0;k<ns;k++) { if(maps[j][k]<0)continue; cr=i|val[st[k]]; if(dp[cr][k]==-1)dp[cr][k]=dp[i][j]+maps[j][k]; else dp[cr][k]=min(dp[cr][k],dp[i][j]+maps[j][k]); // cout<<cr<<" "<<k<<" "<<dp[cr][k]<<endl; } } } int minn=-1; cr=(1<<n)-1; for(int i=0;i<ns;i++) { if(dp[cr][i]==-1)continue; if(minn<0)minn=dp[cr][i]; minn=min(minn,dp[cr][i]); } cout<<minn<<endl; } }AC; char tmp[1550]; int main() { AC.init(); int n,m,k,x; int T; T=0; while(~scanf("%d%d",&n,&m)&&(n||m)) { AC.reset(); for(int i=1;i<=n;i++) { scanf("%s",tmp); AC.insert(tmp,1<<(i-1),0); } for(int i=1;i<=m;i++) { scanf("%s",tmp); AC.insert(tmp,1,1); } AC.ac_build(); AC.work(n); } return 0; }