AC自动机专题小结
2014-07-09 11:56
369 查看
唔.....AC自动机的资料觉得大白书应该是很不错的资料的,再推荐一篇大牛的blog
http://blog.csdn.net/niushuai666/article/details/7002823
在理解了KMP之后相信AC自动机也不会有什么太大的问题
而与KMP之间的出入也就在于,KMP中可以直接定位具体是哪一个字符(即每一个节点都是代表了某一个确认的字符)
而AC自动机是以trie树为基础建立的,因此是通过确认某一个字符的指针是否非空来确定该字符是否存在(即可以把每一个点看做一个传送门集合地)
这样会不会好理解一点呢?(个人观点仅供参考)
下面仍然是题目
【HDU 2222】Keywords Search
很基础的AC自动机了,没什么好说的
这道题的主要问题就是可见字符是包括空格的
因此要用gets
还是模版题,写这种题的时候目的只是为了理解+熟悉模版
【HDU 2243】单词情结
感谢wzm学长对我的悉心教导,才让我能做出第一道AC自动机+dp
这道题一看数据L有2^31-1这么多,应该想到用矩阵加速
而需要求得是所有小于L满足条件的式子,因此需要增加一维来保存总的结果
至于矩阵的构造沿着AC自动机跑一遍就知道了
看字符串的个数就可以想到应该用状压DP
然后可以先预处理出每一个状态有多少个单词
最后再统计一下单词数>=k的就get了
(这道题我看了题解我会乱说?_(:з」∠)_)
ORZ之前一直没有想过这样的状态压缩,一直习惯于所有参与的个数都是相同因此同进制
这道题还是给了我很大的启发吧,学习了
其实题目也不会难,首先想到的便是记忆化搜索,但是时间卡得太紧做不到,因此改用递推就可过
重点是状态的保存。状态的压缩目的主要是为了几个参数之间能够不被相互干扰,因此想到用n进制来表示各个参数之间的状态
而这道题很明显用41进制会爆掉,再者A+C+G+T不会超过40,因此就用混合进制(_(:з」∠)_我乱取的名字不要打我)的方法保存就行了
代码略丑_(:з」∠)_,(刚尝试用结构体来写很好玩呀~后面一直都是结构体了)
【HDU 2296】 Ring
这道题就是一个一般的AC自动机+DP的题,难点就在于要输出最短,且字典序最小的题
加一个ans数组保存一下就行了(我调了一下午难道我会乱说?T_T)
在储存节点的值的时候,开始还想着用结构体来保存,但是后来发现根本没必要啊- =。
不需要区分到底是哪一个串,直接把值加上去就行了_(:з」∠)_
【HDU 2457】DNA repair
这道题就是尽量沿着原串在AC自动机上走去走去的,如果和原串不相同就val+1
DP记录一下就可以过了
专题里面的压轴题了吧,卡了我好几天,最后还是看题解_(:з」∠),果然还是太弱了
因为直接在AC自动机上跑去跑去的会做大量重复的工作,因此直接预处理出串与串之间的dis然后状压DP就OK了
因为才做了西安邀请赛的重现,写了1010才反应过来这个题该怎么来,感人肺腑啊。。。
HDU的题差不多贴完了,吃饭去一会儿再来!
下面是几道其他OJ的题
【ZOJ 3430】Detect the Virus
这道题的难点其实是在于编码的解密吧,然后就是很简单的AC自动机的匹配了
这道题的主要是要记录该串在上一次遇到的位置,然后就可以确定是否重叠了
【POJ 2778】DNA Sequence
这道题是很基础的在AC自动机上面跑DP的题了
也是需要用到矩阵,是HDU 2243的简化版吧
【POJ 1625】Censored!
这道题在想法上没什么多说的,主要是练习了一下高精度的使用(竟然被高精度坑了有1个多小时_(:з」∠)_)
好了差不多就只能刷这么多题了T_T
http://blog.csdn.net/niushuai666/article/details/7002823
在理解了KMP之后相信AC自动机也不会有什么太大的问题
而与KMP之间的出入也就在于,KMP中可以直接定位具体是哪一个字符(即每一个节点都是代表了某一个确认的字符)
而AC自动机是以trie树为基础建立的,因此是通过确认某一个字符的指针是否非空来确定该字符是否存在(即可以把每一个点看做一个传送门集合地)
这样会不会好理解一点呢?(个人观点仅供参考)
下面仍然是题目
【HDU 2222】Keywords Search
很基础的AC自动机了,没什么好说的
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int SIZEN=500005; int ch[SIZEN][26],sz; int f[SIZEN],last[SIZEN]; int val[SIZEN],ret; char str[SIZEN<<1]; int idx(char c){ return c-'a'; } void Insert(char s[]){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++) { int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[sz])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]++; } void add(int j){ while(j){ ret+=val[j]; val[j]=0; j=last[j]; } } void Find(char s[]){ int len=strlen(s); int j=0; for(int i=0;i<len;i++){ int c=idx(s[i]); while(j&&!ch[j][c]) j=f[j]; j=ch[j][c]; if(val[j]) {add(j);} else if(last[j]) {add(last[j]);} } } void getfail(){ queue<int> q; f[0]=0; for(int c=0;c<26;c++){ int u=ch[0][c]; if(u) {f[u]=0;q.push(u);last[u]=0;} } while(!q.empty()){ int r=q.front();q.pop(); for(int c=0;c<26;c++){ int u=ch[r][c]; if(!u) continue; q.push(u); int v=f[r]; while(v&&!ch[v][c]) v=f[v]; f[u]=ch[v][c]; last[u]=val[f[u]]?f[u]:last[f[u]]; } } } void init(){ memset(val,0,sizeof(val)); memset(ch[0],0,sizeof(ch[0])); ret=0; sz=1; } int main() { //freopen("data.in","r",stdin); int i,j; int _,n; scanf("%d",&_); while(_--){ init(); scanf("%d",&n); for(i=0;i<n;i++) { scanf("%s",str); Insert(str); } getfail(); scanf("%s",str); Find(str); printf("%d\n",ret); } return 0; }【HDU 2896】病毒侵袭
这道题的主要问题就是可见字符是包括空格的
因此要用gets
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int SIZEN=80005; char str[SIZEN]; int ch[SIZEN][100],sz; int f[SIZEN],last[SIZEN]; int val[SIZEN],flag[600]; void init(){ memset(ch,0,sizeof(ch)); memset(val,0,sizeof(val)); sz=1; } int idx(char c){ return c-32; } void Insert(char s[],int v){ int len=strlen(str); int rt=0; for(int i=0;i<len;i++){ int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(sz)); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]=v; } void Insert(char s[]){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++) { int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[sz])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]++; } void getfail(){ queue<int> q; f[0]=0; for(int c=0;c<100;c++){ int u=ch[0][c]; if(u) {f[u]=0;q.push(u);last[u]=0;} } while(!q.empty()){ int r=q.front();q.pop(); for(int c=0;c<100;c++){ int u=ch[r][c]; if(!u) {ch[r][c]=ch[f[r]][c];continue;} q.push(u); int v=f[r]; while(v&&!ch[v][c]) v=f[v]; f[u]=ch[v][c]; last[u]=val[f[u]]?f[u]:last[f[u]]; } } } void add(int j){ while(j){ flag[val[j]]=1; j=last[j]; } } void Find(char s[]){ int len=strlen(s); int j=0; for(int i=0;i<len;i++){ int c=idx(s[i]); j=ch[j][c]; if(val[j]) {add(j);} else if(last[j]) {add(last[j]);} } } void output(int id){ printf("web %d: ",id); int tflag=0; for(int i=1;i<600;i++){ if(tflag&&flag[i]) printf(" %d",i); else if(flag[i]){ tflag=1; printf("%d",i); } } printf("\n"); } int main() { //freopen("data.in","r",stdin); int i,j; int n,m; int cnt; while(scanf("%d%*c",&n)!=EOF){ init(); for(i=0;i<n;i++){ gets(str); Insert(str,i+1); } getfail(); scanf("%d%*c",&m); cnt=0; for(i=0;i<m;i++){ gets(str); memset(flag,0,sizeof(flag)); Find(str); int tt=0; for(j=1;j<=n;j++){ if(flag[j]){ tt=1; break; } } if(tt){ cnt++; output(i+1); } } printf("total: %d\n",cnt); } return 0; }【HDU 3065】 病毒侵袭持续中
还是模版题,写这种题的时候目的只是为了理解+熟悉模版
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int SIZEN=50005; int ch[SIZEN][26],sz; int f[SIZEN],last[SIZEN]; int val[SIZEN],cnt[1005]; char str[2000005]; char virus[1005][55]; void init(){ memset(ch[0],0,sizeof(ch[0])); memset(val,0,sizeof(val)); memset(cnt,0,sizeof(cnt)); sz=1; } int idx(char c){ return c-'A'; } void Insert(char s[],int v){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(str[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[sz])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]=v; } void add(int j){ while(j){ cnt[val[j]]++; j=last[j]; } } void Find(char s[]){ int len=strlen(s); int j=0; for(int i=0;i<len;i++){ if(!(str[i]<='Z'&&str[i]>='A')){ j=0; continue; } int c=idx(str[i]); while(j&&!ch[j][c]) j=f[j]; j=ch[j][c]; if(val[j]) add(j); else if(last[j]) add(last[j]); } } void getfail(){ queue<int> q; f[0]=0; for(int c=0;c<26;c++){ int u=ch[0][c]; if(u){f[u]=0;q.push(u);last[u]=0;} } while(!q.empty()){ int r=q.front();q.pop(); for(int c=0;c<26;c++){ int u=ch[r][c]; if(!u) continue; q.push(u); int v=f[r]; while(v&&!ch[v][c]) v=f[v]; f[u]=ch[v][c]; last[u]=val[f[u]]?f[u]:last[f[u]]; } } } int main() { //freopen("data.in","r",stdin); int i,j; int n; while(scanf("%d",&n)!=EOF){ init(); for(i=1;i<=n;i++){ scanf("%s%*c",str); Insert(str,i); strcpy(virus[i],str); } getfail(); gets(str); Find(str); for(i=1;i<=n;i++){ if(cnt[i]) printf("%s: %d\n",virus[i],cnt[i]); } } }
【HDU 2243】单词情结
感谢wzm学长对我的悉心教导,才让我能做出第一道AC自动机+dp
这道题一看数据L有2^31-1这么多,应该想到用矩阵加速
而需要求得是所有小于L满足条件的式子,因此需要增加一维来保存总的结果
至于矩阵的构造沿着AC自动机跑一遍就知道了
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; typedef unsigned long long ULL; typedef long long LL; const int SIZEN=40; int ch[SIZEN][26],sz; int f[SIZEN],val[SIZEN]; int cc[SIZEN],n; char str[SIZEN]; struct Mat{ ULL mat[SIZEN][SIZEN]; void init(){ memset(mat,0,sizeof(mat)); } void output(){ for(int i=0;i<=sz;i++){ for(int j=0;j<=sz;j++) printf("%2d ",mat[i][j]); printf("\n"); } } void getE(){ memset(mat,0,sizeof(mat)); for(int i=0;i<=sz;i++) mat[i][i]=1; } }; Mat A; Mat operator *(Mat a,Mat b){ Mat ret; ret.init(); int i,j,k; for(i=0;i<=sz;i++){ for(j=0;j<=sz;j++) for(k=0;k<=sz;k++) ret.mat[i][j]+=a.mat[i][k]*b.mat[k][j]; } return ret; } Mat pow(Mat a,LL p){ Mat ret; ret.getE(); while(p){ if(p&1) ret=ret*a; a=a*a; p>>=1; } return ret; } int idx(char c){ return c-'a'; } void Insert(char s[],int v){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[sz])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]=v; } void getfail(){ queue<int> q; f[0]=0; for(int c=0;c<26;c++){ int u=ch[0][c]; if(u){f[u]=0;q.push(u);} else f[u]=0; } while(!q.empty()){ int r=q.front();q.pop(); if(val[f[r]]) val[r]=1; for(int c=0;c<26;c++){ int u=ch[r][c]; if(!u) {ch[r][c]=ch[f[r]][c];continue;} q.push(u); int v=f[r]; while(v&&!ch[v][c]) v=f[v]; f[u]=ch[v][c]; } } } void init(){ memset(ch[0],0,sizeof(ch[0])); memset(val,0,sizeof(val)); sz=1; } int main() { //freopen("data.in","r",stdin); int i,j; int n; LL l; while(scanf("%d %I64d",&n,&l)!=EOF){ init(); for(i=0;i<n;i++){ scanf("%s",str); Insert(str,1); } getfail(); //for(i=0;i<30;i++) printf("%d ",val[i]);printf("\n"); A.init(); for(i=0;i<sz;i++){ if(val[i]) continue; for(j=0;j<26;j++){ if(val[ch[i][j]]) A.mat[i][sz]++; else A.mat[i][ch[i][j]]++; } } A.mat[sz][sz]=26; sz++;A.mat[sz][sz]=A.mat[sz-1][sz]=1; //A.output(); Mat ret=pow(A,l+1); printf("%I64u\n",ret.mat[0][sz]); } return 0; } /* 25 1 0 0 0 24 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 26*/ /* 25 1 0 24 0 2 0 0 26 */【HDU 2825】Wireless Password
看字符串的个数就可以想到应该用状压DP
然后可以先预处理出每一个状态有多少个单词
最后再统计一下单词数>=k的就get了
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> #define MOD 20090717 using namespace std; const int SIZEN=205; int idx(char c){ return c-'a'; } int ch[SIZEN][26]; int dp[2][SIZEN][1040]; int inc[1040],sz,val[SIZEN]; int f[SIZEN]; char str[200]; void init(){ memset(ch[0],0,sizeof(ch[0])); memset(val,0,sizeof(val)); memset(dp,0,sizeof(dp)); sz=1; } void Insert(char s[],int v){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[sz])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]|=(1<<v); } void getfail(){ queue<int> q; f[0]=0; for(int c=0;c<26;c++){ int u=ch[0][c]; if(u){f[u]=0;q.push(u);} else f[u]=0; } while(!q.empty()){ int r=q.front();q.pop(); val[r]=val[r]|val[f[r]]; for(int c=0;c<26;c++){ int u=ch[r][c]; if(!u) {ch[r][c]=ch[f[r]][c];continue;} else{ f[u]=ch[f[r]][c]; q.push(u); } } } } void getinc(){ for(int i=0;i<(1<<10);i++){ int t=i; while(t){ inc[i]+=t&1; t>>=1; } } } int main() { //freopen("data.in","r",stdin); int i,j,k,l; int n,m,lim; int cnt=0; getinc(); while(scanf("%d%d%d",&n,&m,&lim)!=EOF){ if(!n&&!m&&!lim) break; init(); cnt=1<<m; for(i=0;i<m;i++){ scanf("%s",str); Insert(str,i); } getfail(); dp[0][0][0]=1; for(i=0;i<n;i++){ memset(dp[(i+1)%2],0,sizeof(dp[(i+1)%2])); for(j=0;j<sz;j++){ for(l=0;l<cnt;l++){ if(dp[i%2][j][l]==0) continue; for(k=0;k<26;k++){ int nj=ch[j][k]; int nl=val[nj]|l; int& t_dp=dp[(i+1)%2][nj][nl]; t_dp=(t_dp+dp[i%2][j][l])%MOD; } } } } int ans=0; for(int l=0;l<(1<<m);l++){ if(inc[l]>=lim){ for(j=0;j<sz;j++){ ans=(dp[n%2][j][l]+ans)%MOD; } } } printf("%d\n",ans); } return 0; }【HDU 3341】Lost's revenge
(这道题我看了题解我会乱说?_(:з」∠)_)
ORZ之前一直没有想过这样的状态压缩,一直习惯于所有参与的个数都是相同因此同进制
这道题还是给了我很大的启发吧,学习了
其实题目也不会难,首先想到的便是记忆化搜索,但是时间卡得太紧做不到,因此改用递推就可过
重点是状态的保存。状态的压缩目的主要是为了几个参数之间能够不被相互干扰,因此想到用n进制来表示各个参数之间的状态
而这道题很明显用41进制会爆掉,再者A+C+G+T不会超过40,因此就用混合进制(_(:з」∠)_我乱取的名字不要打我)的方法保存就行了
代码略丑_(:з」∠)_,(刚尝试用结构体来写很好玩呀~后面一直都是结构体了)
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int SIZEN=505; struct AC{ int ch[SIZEN][4],sz; int f[SIZEN],val[SIZEN]; char str[SIZEN]; int pow[10],cnt[4]; int dp[SIZEN][16000]; void getpow(){ pow[0]=1; for(int i=1;i<4;i++) pow[i]=pow[i-1]*(cnt[i-1]+1); pow[4]=10000000; } void init(){ memset(ch[0],0,sizeof(ch[0])); memset(val,0,sizeof(val)); memset(cnt,0,sizeof(cnt)); sz=1; } int idx(char c){ if(c=='A') return 0; else if(c=='C') return 1; else if(c=='G') return 2; else if(c=='T') return 3; } void insert(char s[],int v){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[sz])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]+=v; } void getfail(){ queue<int> q; f[0]=0; for(int c=0;c<4;c++){ int u=ch[0][c]; if(u) q.push(u);f[u]=0; } while(!q.empty()){ int r=q.front();q.pop(); if(val[f[r]]) val[r]+=val[f[r]]; for(int c=0;c<4;c++){ int u=ch[r][c]; if(!u) {ch[r][c]=ch[f[r]][c];continue;} f[u]=ch[f[r]][c]; q.push(u); } } } int dfs(int u,int s){ if(dp[u][s]!=-1) return dp[u][s]; int ans=0; for(int i=0;i<4;i++){ if(s%pow[i+1]/pow[i]==0) continue; ans=max(ans,dfs(ch[u][i],s-pow[i])); } ans+=val[u]; return dp[u][s]=ans; } int DP(int ss){ dp[0][0]=0; for(int i=0;i<ss;i++){ for(int j=0;j<sz;j++) if(dp[j][i]+1){ for(int k=0;k<4;k++){ if((i%pow[k+1]/pow[k]+1)>cnt[k]) continue; if(i+pow[k]>ss) continue; int new_j=ch[j][k]; int new_i=i+pow[k]; int &t=dp[new_j][new_i]; t=max(t,dp[j][i]+val[new_j]); } } } int ans=0; for(int i=0;i<sz;i++) ans=max(ans,dp[i][ss]); return ans; } int solve(int n){ init(); for(int i=0;i<n;i++){ scanf("%s",str); insert(str,1); } getfail(); scanf("%s",str); int len=strlen(str); for(int i=0;i<len;i++) cnt[idx(str[i])]++; getpow(); int ss=cnt[0]*pow[0]+cnt[1]*pow[1]+cnt[2]*pow[2]+cnt[3]*pow[3]; memset(dp,-1,sizeof(dp[0])*(sz+3)); return DP(ss); } }; AC ac; int main() { //freopen("data.in","r",stdin); int i,j; int n,txt=1; while(scanf("%d",&n)!=EOF&&n) printf("Case %d: %d\n",txt++,ac.solve(n)); }
【HDU 2296】 Ring
这道题就是一个一般的AC自动机+DP的题,难点就在于要输出最短,且字典序最小的题
加一个ans数组保存一下就行了(我调了一下午难道我会乱说?T_T)
在储存节点的值的时候,开始还想着用结构体来保存,但是后来发现根本没必要啊- =。
不需要区分到底是哪一个串,直接把值加上去就行了_(:з」∠)_
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int SIZEN=1005; char str[105][20]; int v[105]; struct AC{ int ch[SIZEN][26],sz; int f[SIZEN]; int val[SIZEN]; int dp_val[55][SIZEN]; char stat[SIZEN]; char ans[55][SIZEN][55]; void init(){ memset(ans,0,sizeof(ans)); memset(dp_val,-1,sizeof(dp_val)); memset(ch[0],0,sizeof(ch[0])); memset(val,0,sizeof(val)); sz=1; } int idx(char c){ return c-'a'; } void insert(char s[],int id){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[sz])); stat[sz]=s[i]; ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]+=v[id]; } void getfail(){ queue<int> q; f[0]=0; for(int i=0;i<26;i++){ int u=ch[0][i]; if(u) q.push(u); f[u]=0; } while(!q.empty()){ int r=q.front();q.pop(); val[r]=val[r]+val[f[r]]; for(int c=0;c<26;c++){ int u=ch[r][c]; if(!u) {ch[r][c]=ch[f[r]][c];continue;} f[u]=ch[f[r]][c]; q.push(u); } } } void solve(int n){ dp_val[0][0]=0; for(int i=0;i<n;i++){ for(int j=0;j<sz;j++) if(dp_val[i][j]!=-1){ for(int k=0;k<26;k++){ int ni=i+1; int nj=ch[j][k]; int t_val=dp_val[i][j]+val[nj]; char t_ans[55]; strcpy(t_ans,ans[i][j]); t_ans[i]='a'+k;t_ans[i+1]='\0'; if(t_val>dp_val[ni][nj]||(t_val==dp_val[ni][nj]&&strcmp(ans[ni][nj],t_ans)>0)){ dp_val[ni][nj]=t_val; strcpy(ans[ni][nj],t_ans); } } } } int tans=0; char tstat[55]; for(int i=0;i<=n;i++){ for(int j=0;j<sz;j++){ if(dp_val[i][j]==-1) continue; if(tans<dp_val[i][j]){ strcpy(tstat,ans[i][j]); tans=dp_val[i][j]; } else if(strlen(tstat)<strlen(ans[i][j])) continue; else if(tans==dp_val[i][j]&&strcmp(tstat,ans[i][j])>0){ tans=dp_val[i][j]; strcpy(tstat,ans[i][j]); } } } printf("%s\n",tstat); } }; AC ac; int main() { //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); int i,j; int n,m,_; scanf("%d",&_); while(_--){ scanf("%d%d",&n,&m); ac.init(); memset(v,0,sizeof(v)); for(i=0;i<m;i++){ scanf("%s",&str[i]); } for(i=1;i<=m;i++) scanf("%d",&v[i]);v[0]=0; for(i=0;i<m;i++) ac.insert(str[i],i+1); ac.getfail(); ac.solve(n); } return 0; }
【HDU 2457】DNA repair
这道题就是尽量沿着原串在AC自动机上走去走去的,如果和原串不相同就val+1
DP记录一下就可以过了
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> #define INF 2139062143 using namespace std; const int SIZEN=1005; struct AC{ int ch[SIZEN][4],sz; int f[SIZEN],val[SIZEN]; char ss[SIZEN]; int dp[SIZEN][SIZEN]; void init(){ memset(val,0,sizeof(val)); memset(ch[0],0,sizeof(ch[0])); memset(dp,127,sizeof(dp)); sz=1; } int idx(char c){ if(c=='A') return 0; if(c=='G') return 1; if(c=='C') return 2; if(c=='T') return 3; } void insert(char s[]){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[sz])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]++; } void getfail(){ queue<int> q; f[0]=0; for(int c=0;c<4;c++){ int u=ch[0][c]; if(u) q.push(u);f[u]=0; } while(!q.empty()){ int r=q.front();q.pop(); if(val[f[r]]) val[r]+=val[f[r]]; for(int c=0;c<4;c++){ int u=ch[r][c]; if(!u) {ch[r][c]=ch[f[r]][c];continue;} f[u]=ch[f[r]][c]; q.push(u); } } } int solve(){ int len=strlen(ss); dp[0][0]=0; for(int i=0;i<len;i++){ for(int j=0;j<sz;j++){ for(int k=0;k<4;k++){ if(val[ch[j][k]]) continue; int nj=ch[j][k]; int ni=i+1; int t_val=dp[i][j]; if(idx(ss[i])!=k) t_val++; dp[ni][nj]=min(dp[ni][nj],t_val); } } } int ans=INF; for(int i=0;i<sz;i++) ans=min(ans,dp[len][i]); if(ans==INF) return -1; return ans; } }; AC ac; int main() { //freopen("data.in","r",stdin); int i,j; int n,txt=1; char str[SIZEN]; while(scanf("%d",&n)!=EOF&&n){ ac.init(); for(i=0;i<n;i++){ scanf("%s",str); ac.insert(str); } ac.getfail(); scanf("%s",ac.ss); printf("Case %d: %d\n",txt++,ac.solve()); } return 0; }【HDU 3247】Resource Archiver
专题里面的压轴题了吧,卡了我好几天,最后还是看题解_(:з」∠),果然还是太弱了
因为直接在AC自动机上跑去跑去的会做大量重复的工作,因此直接预处理出串与串之间的dis然后状压DP就OK了
因为才做了西安邀请赛的重现,写了1010才反应过来这个题该怎么来,感人肺腑啊。。。
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int SIZEN=60005; const int INF=1000005; char str[1005]; struct AC{ int ch[SIZEN][2],sz; int val[SIZEN],f[SIZEN]; int dis[15][15]; int dp[15][1040],len[20]; void init(){ for(int i=0;i<15;i++) for(int j=0;j<15;j++) dis[i][j]=i==j?0:INF; for(int i=0;i<15;i++) for(int j=0;j<1040;j++) dp[i][j]=INF; memset(val,0,sizeof(val)); memset(ch[0],0,sizeof(ch[0])); sz=1; } int idx(char c){ return c-'0'; } int insert(char s[],int v){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[sz])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]=v; return rt; } void getfail(){ queue<int> q; f[0]=0; for(int i=0;i<2;i++){ int u=ch[0][i]; if(u) q.push(u); f[u]=0; } while(!q.empty()){ int r=q.front();q.pop(); if(val[f[r]]==-1) val[r]=-1; for(int i=0;i<2;i++){ int u=ch[r][i]; if(!u) {ch[r][i]=ch[f[r]][i];continue;} f[u]=ch[f[r]][i]; q.push(u); } } } void query(int s){ queue<int> q; int dd[SIZEN]; memset(dd,0,sizeof(dd)); q.push(s); int ss=val[s]-1; while(!q.empty()){ int u=q.front();q.pop(); for(int i=0;i<2;i++){ int v=ch[u][i]; if(dd[v]||v==s) continue; if(val[v]==-1) continue; dd[v]=dd[u]+1; q.push(v); if(val[v]) dis[ss][val[v]-1]=dd[v]; } } } int solve(int n){ for(int i=0;i<n;i++) dis[i][i]=0; for(int i=0;i<n;i++) dp[i][1<<i]=len[i]; for(int i=1;i<(1<<n);i++){ for(int j=0;j<n;j++){ for(int k=0;k<n;k++){ int ni=i|(1<<k); dp[k][ni]=min(dp[k][ni],dp[j][i]+dis[j][k]); } } } int ans=INF; for(int i=0;i<n;i++) ans=min(ans,dp[i][(1<<n)-1]); return ans; } void debug(int n){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++) printf("%7d ",dis[i][j]); printf("\n"); } } }; AC ac; int pos[20]; int main() { int i,j; int n,m; while(scanf("%d%d",&n,&m)!=EOF){ if(n==0&&m==0) break; ac.init(); for(i=0;i<n;i++){ scanf("%s",str); pos[i]=ac.insert(str,i+1); ac.len[i]=strlen(str); } /*for(i=0;i<ac.sz;i++) printf("%2d ",i);printf("\n"); for(i=0;i<ac.sz;i++) printf("%2d ",ac.val[i]);printf("\n"); for(i=0;i<=n;i++) printf("%d ",pos[i]);printf("\n");*/ for(i=0;i<m;i++){ scanf("%s",str); ac.insert(str,-1); } ac.getfail(); for(i=0;i<n;i++) ac.query(pos[i]); //ac.debug(n); printf("%d\n",ac.solve(n)); } return 0; }
HDU的题差不多贴完了,吃饭去一会儿再来!
下面是几道其他OJ的题
【ZOJ 3430】Detect the Virus
这道题的难点其实是在于编码的解密吧,然后就是很简单的AC自动机的匹配了
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int SIZEN=50500; char str[SIZEN]; int ret[SIZEN]; bool bit[SIZEN]; struct AC{ int ch[SIZEN][256],sz; int f[SIZEN],last[SIZEN]; int val[SIZEN]; bool flag[1000]; void init(){ memset(ch[0],0,sizeof(ch[0])); memset(last,0,sizeof(last)); memset(val,0,sizeof(val)); sz=1; } void insert(int s[],int v,int len){ int rt=0; for(int i=0;i<len;i++){ int t=s[i]; if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[0])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]=v; } void getfail(){ queue<int> q; f[0]=0; for(int i=0;i<256;i++){ int u=ch[0][i]; if(u) q.push(u); f[u]=0; } while(!q.empty()){ int r=q.front();q.pop(); for(int c=0;c<256;c++){ int u=ch[r][c]; if(!u) {ch[r][c]=ch[f[r]][c];continue;} f[u]=ch[f[r]][c]; q.push(u); last[u]=val[f[u]]?f[u]:last[f[u]]; } } } void add(int j){ while(j){ flag[val[j]]=1; j=last[j]; } } void Find(int s[],int len){ int j=0; for(int i=0;i<len;i++){ int c=s[i]; j=ch[j][c]; if(val[j]) add(j); else if(last[j]) add(last[j]); } } }; AC ac; int idx(char c){ if(c<='Z'&&c>='A') return c-'A'; if(c<='z'&&c>='a') return c-'a'+26; if(c<='9'&&c>='0') return c-'0'+52; if(c=='+') return 62; if(c=='-') return 63; } int trans(){ int i,j; int cnt=0; for(i=0;str[i]!='\0'&&str[i]!='=';i++){ int t=idx(str[i]); for(j=5;j>=0;j--) bit[cnt++]=t&(1<<j)?1:0; } for(;str[i]!='\0';i++) if(str[i]=='=') cnt-=2; for(i=0;i<cnt/8;i++){ ret[i]=0; for(j=0;j<8;j++) ret[i]+=bit[i*8+j]<<(7-j); } return cnt/8; } int main() { //freopen("data.in","r",stdin); int i,j; int n,m; while(scanf("%d",&n)!=EOF){ ac.init(); for(i=0;i<n;i++){ scanf("%s",str); int len=trans(); ac.insert(ret,i+1,len); } ac.getfail(); scanf("%d",&m); for(i=0;i<m;i++){ scanf("%s",str); memset(ac.flag,0,sizeof(ac.flag)); int len=trans(); ac.Find(ret,len); int ans=0; for(j=1;j<=n;j++) ans+=ac.flag[j]; printf("%d\n",ans); } printf("\n"); } return 0; }【ZOJ 3228】Searching the String
这道题的主要是要记录该串在上一次遇到的位置,然后就可以确定是否重叠了
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int SIZEN=100005*6; char str[SIZEN],ss[20]; struct AC{ int ch[SIZEN][26],sz; int f[SIZEN],ll[SIZEN]; int cnt[2][SIZEN],val[SIZEN]; int dep[SIZEN],last[SIZEN]; void init(){ memset(last,0,sizeof(last)); memset(val,0,sizeof(val)); memset(ch[0],0,sizeof(ch[0])); memset(cnt,0,sizeof(cnt)); memset(ll,-1,sizeof(ll)); sz=1;dep[0]=0; } int idx(char c){ return c-'a'; } int insert(char s[]){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[0])); dep[sz]=dep[rt]+1; ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]=1; return rt; } void getfail(){ queue<int> q; f[0]=0; for(int i=0;i<26;i++){ int u=ch[0][i]; if(u) q.push(u); f[u]=0; } while(!q.empty()){ int r=q.front();q.pop(); for(int i=0;i<26;i++){ int u=ch[r][i]; if(!u) {ch[r][i]=ch[f[r]][i];continue;} f[u]=ch[f[r]][i]; q.push(u); last[u]=val[f[u]]?f[u]:last[f[u]]; } } } void add(int j,int loc){ while(j){ cnt[0][j]++; if(loc-ll[j]>=dep[j]){ cnt[1][j]++; ll[j]=loc; } j=last[j]; } } void find(char s[]){ int len=strlen(s); int j=0; for(int i=0;i<len;i++){ int c=idx(s[i]); j=ch[j][c]; add(j,i); } } }; AC ac; int pos[SIZEN],type[SIZEN]; int txt=1; int main() { //freopen("data.in","r",stdin); int i,j; int n; while(scanf("%s",str)!=EOF){ scanf("%d",&n); ac.init(); for(i=0;i<n;i++){ scanf("%d %s",&type[i],ss); pos[i]=ac.insert(ss); } ac.getfail(); ac.find(str); printf("Case %d\n",txt++); for(i=0;i<n;i++) printf("%d\n",ac.cnt[type[i]][pos[i]]); printf("\n"); } return 0; }
【POJ 2778】DNA Sequence
这道题是很基础的在AC自动机上面跑DP的题了
也是需要用到矩阵,是HDU 2243的简化版吧
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> #define mod 100000 using namespace std; typedef long long LL; const int SIZEN=110; char str[100]; struct Mat{ LL mat[SIZEN][SIZEN]; void init(){ memset(mat,0,sizeof(mat)); } void getE(){ memset(mat,0,sizeof(mat)); for(int i=0;i<SIZEN;i++) mat[i][i]=1; } void output(int sz){ for(int i=0;i<sz;i++){ for(int j=0;j<sz;j++) printf("%I64d ",mat[i][j]); printf("\n"); } } }; struct AC{ int ch[SIZEN][4],sz; int f[SIZEN],val[SIZEN]; int idx(char c){ if(c=='A') return 0; if(c=='G') return 1; if(c=='C') return 2; if(c=='T') return 3; } void init(){ memset(ch[0],0,sizeof(ch[0])); memset(val,0,sizeof(val)); sz=1; } void insert(char s[]){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(str[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[0])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]=1; } void getfail(){ queue<int> q; f[0]=0; for(int i=0;i<4;i++){ int u=ch[0][i]; if(u) {q.push(u);f[u]=0;} } while(!q.empty()){ int r=q.front();q.pop(); if(val[f[r]]) val[r]=1; for(int i=0;i<4;i++){ int u=ch[r][i]; if(!u) {ch[r][i]=ch[f[r]][i];continue;} f[u]=ch[f[r]][i]; q.push(u); } } } }; AC ac; Mat A; Mat operator *(Mat a,Mat b){ Mat ret; ret.init(); for(int i=0;i<=ac.sz;i++) for(int j=0;j<=ac.sz;j++) for(int k=0;k<=ac.sz;k++){ ret.mat[i][j]=(ret.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod; } return ret; } Mat operator ^(Mat a,int p){ Mat ret; ret.getE(); while(p){ if(p&1) ret=ret*a; a=a*a; p>>=1; } return ret; } LL pow(int b){ LL ret=1,a=4; while(b){ if(b&1) ret=ret*a%mod; a=a*a%mod; b>>=1; } return ret; } int main() { int i,j; int n,m; while(scanf("%d%d",&m,&n)!=EOF){ ac.init(); for(i=0;i<m;i++){ scanf("%s",str); ac.insert(str); } ac.getfail(); A.init(); for(i=0;i<ac.sz;i++){ if(ac.val[i]) continue; for(j=0;j<4;j++){ int nj=ac.ch[i][j]; if(ac.val[nj]) A.mat[i][ac.sz]++; else A.mat[i][nj]++; } } A.mat[ac.sz][ac.sz]=4; //A.output(ac.sz+2); Mat ret=A^n; //ret.output(ac.sz+2); LL ans=(pow(n)-ret.mat[0][ac.sz]+mod)%mod; printf("%I64d\n",ans); } return 0; }
【POJ 1625】Censored!
这道题在想法上没什么多说的,主要是练习了一下高精度的使用(竟然被高精度坑了有1个多小时_(:з」∠)_)
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> #define mod 10000000 using namespace std; const int SIZEN=155; char str[SIZEN]; struct Big{ int num[205]; void init(){ memset(num,0,sizeof(num)); } void output(){ int j=204; while(!num[j]&&j>0) j--; printf("%d",num[j--]); while(j>=0) printf("%07d",num[j--]); printf("\n"); } }; Big operator + (Big a,Big b){ Big ret; ret.init(); for(int i=0;i<SIZEN;i++) ret.num[i]=a.num[i]+b.num[i]; for(int i=0;i<SIZEN-1;i++){ ret.num[i+1]+=ret.num[i]/mod; ret.num[i]%=mod; } return ret; } struct AC{ char ss[SIZEN]; int ch[105][51],sz; int f[105],val[105]; int map[515]; Big dp[51][105]; void init(){ memset(map,-1,sizeof(map)); memset(val,0,sizeof(val)); memset(ch[0],0,sizeof(ch[0])); for(int i=0;i<51;i++) for(int j=0;j<105;j++) dp[i][j].init(); sz=1; } void getmap(){ gets(ss); int len=strlen(ss); for(int i=0;i<len;i++) map[ss[i]+128]=i; } int idx(char c){ return map[c+128]; } void insert(char s[],int v){ int len=strlen(s); int rt=0; for(int i=0;i<len;i++){ int t=idx(s[i]); if(!ch[rt][t]){ memset(ch[sz],0,sizeof(ch[0])); ch[rt][t]=sz++; } rt=ch[rt][t]; } val[rt]=v; } void getfail(int n){ queue<int> q; f[0]=0; for(int i=0;i<n;i++){ int u=ch[0][i]; if(u) q.push(u);f[u]=0; } while(!q.empty()){ int r=q.front();q.pop(); if(val[f[r]]) val[r]=val[f[r]]; for(int i=0;i<n;i++){ int u=ch[r][i]; if(!u) {ch[r][i]=ch[f[r]][i];continue;} f[u]=ch[f[r]][i]; q.push(u); } } } void solve(int n,int m){ dp[0][0].num[0]=1; for(int i=0;i<m;i++){ int ni=i+1; for(int j=0;j<sz;j++) if(val[j]==0){ for(int k=0;k<n;k++){ int nj=ch[j][k]; if(val[nj]) continue; dp[ni][nj]=dp[ni][nj]+dp[i][j]; } } } } }; AC ac; int main() { //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); int i,j; int n,m,p; while(scanf("%d%d%d",&n,&m,&p)!=EOF){ ac.init(); getchar(); ac.getmap(); for(i=0;i<p;i++){ gets(str); ac.insert(str,1); } ac.getfail(n); ac.solve(n,m); Big ans; ans.init(); for(i=0;i<ac.sz;i++) ans=ans+ac.dp[m][i]; ans.output(); } return 0; }
好了差不多就只能刷这么多题了T_T
相关文章推荐
- AC自动机专题小结
- 字符串专题:A - Keywords Search(AC自动机)
- AC自动机专题——C - 病毒侵袭持续中 HDU - 3065
- AC自动机专题
- AC自动机专题
- AC自动机专题训练
- AC自动机专题
- AC自动机专题——F - Censored! POJ - 1625 大数+DP+AC自动机
- AC自动机专题
- 【专题】字符串专题小结(AC自动机 + 后缀自动机)
- AC自动机专题——H - Text Generator SPOJ - GEN 矩阵快速幂+AC自动机
- AC自动机专题
- AC自动机专题——J - DNA repair HDU - 2457 DP+AC自动机
- 初等字符串匹配专题小结[KMP][Manacher][Tire Tree][AC Automation]
- AC自动机专题——K - Ring HDU - 2296 DP+AC自动机
- kuangbin专题十七 ZOJ3228 AC自动机不可重叠计算
- AC自动机专题——L - Wireless Password HDU - 2825 状压DP+AC自动机
- AC自动机专题——P - 考研路茫茫――单词情结 HDU - 2243 矩阵快速幂+AC自动机
- CUGB专题训练之数据结构:F - 病毒侵袭持续中(AC自动机重复子串)
- kuangbin专题十七 HDU2896 病毒侵袭(AC自动机模板题)