今天去上了jsp的上机,感觉以前似乎落下一些知识点。但是没事,我相信我会学会的。。。
今天做了两道AC自动机的题目,感觉跟伸展树一样,AC自动机越来越水了。。。
下午为校赛出了两道题目。虎哥哥告诉我,我要出四道题目。今天先出两道,明天再出两道,爽歪歪。
今天也有一些事情让我很不爽,sad,或许影响到了些许的效率。
AC自动机还有不到10个题,打算这周刷完,然后进军下一个知识点。奋斗吧,小少年。
1,poj-1625- Censored!
我只能说做过E,F这道题目就太水了。
不过这道题目要用到大数的加法。之前大数的加法还写错了一点。真是令人很悲伤。
ans[i][j]:长度为i,状态为j时,有多少种可能性。
ans[i][j]=sum(ans[i-1][k])
#include <cstdio> #include <cstdlib> #include <string> #include <climits> #include <iostream> #include <vector> #include <set> #include <cmath> #include <cctype> #include <algorithm> #include <sstream> #include <map> #include <cstring> #include <queue> using namespace std; #define LL long long const int MAX_NODE = 15*15;//MAX_NODE = StringNumber * StringLength const int CHILD_NUM = 55;//节点个数,一般字符形式的题26个 const int mod = 20090717;//特定题目需要 vector<int>vec; LL dp[501][501]; int ss; struct BIGnumber { int num[101]; int len; void getnum(LL x) { len=0; if(x==0) { num[len++]=0; return; } while(x) { num[len++]=x%10; x=x/10; } } BIGnumber friend operator +(BIGnumber a,BIGnumber b) { int alen=a.len; int blen=b.len; BIGnumber c; c.len=0; int leap=0; int x; int i; for(i=0; i<a.len&&i<b.len; i++) { x=a.num[i]+b.num[i]+leap; c.num[c.len++]=x%10; leap=x/10; } while(i<a.len) { x=a.num[i]+leap; c.num[c.len++]=x%10; leap=x/10; i++; } while(i<b.len) { x=b.num[i]+leap; c.num[c.len++]=x%10; leap=x/10; i++; } if(leap)c.num[c.len++]=leap; return c; } void print() { for(int i=len-1;i>=0;i--) { cout<<num[i]; } cout<<endl; } } ans[55][501],aa,bb; struct ACAutomaton { int chd[MAX_NODE][CHILD_NUM]; int val[MAX_NODE]; int fail[MAX_NODE]; int Q[MAX_NODE]; int ID[256]; int sz; int chnum; void init(char str[]) { fail[0] = 0; int len=strlen(str); chnum=len; for(int i=0; i<len; i++) { ID[str[i]]=i; } } void Reset() { memset(chd[0] , 0 , sizeof(chd[0])); memset(val,0,sizeof(val)); sz = 1; } void insert(char *a,int key) { int p = 0; int len=strlen(a); for (int i=0; i<len; i++) { int c = ID[a[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] = key; } void build_ac() { int *s = Q , *e = Q; for (int i = 0 ; i < chnum; 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]=1; for (int i = 0 ; i < chnum ; i ++) { int &v = chd[u][i]; if (v) { *e ++ = v; fail[v] = chd[ fail[u] ][i]; } else { v = chd[ fail[u] ][i]; } } } } int Work() { memset(dp,0,sizeof(dp)); int i,j; for(i=0; i<sz; i++) { if(val[i])continue; for(j=0; j<chnum; j++) { if(val[chd[i][j]])continue; dp[i][chd[i][j]]++; } } ss=sz; return 0; } void anss(LL m) { for(int i=0;i<=m;i++) { for(int j=0;j<sz;j++)ans[i][j].getnum(0); } ans[0][0].getnum(1); // ans[0][0].print(); for(int i=1; i<=m; i++) { for(int j=0; j<sz; j++) { for(int k=0; k<chnum; k++) { int c=chd[j][k]; if(val[c])continue; ans[i][c]=ans[i][c]+ans[i-1][j]; //ans[i][c].print(); } } } aa.getnum(0); for(int j=0;j<sz;j++) { if(val[j])continue; aa=aa+ans[m][j]; } aa.print(); } } AC; char str[2200000]; char temp[110]; int main() { int n,m,p; while(~scanf("%d%d%d",&n,&m,&p)) { AC.Reset(); scanf("%s",temp); AC.init(temp); for (int i = 1 ; i <=p ; i ++) { scanf("%s",temp); AC.insert(temp,1); } AC.build_ac(); AC.Work(); AC.anss(m); } return 0; }
跟之前的那个题差不多,不过需要记录状态。
dp[i][j][k]:长度为i,节点为j,状态为k的可能性的种数。
用二进制压缩表示k,用滚动数组表示i。
学会了AC自动机,这就是一个模版的过程。
#include<stdio.h> #include<algorithm> #include<iostream> #include<stdlib.h> #include<string.h> using namespace std; const int maxnode=110; const int childnum=26; const int mod=20090717; struct ac_tree { int chd[maxnode][childnum]; int val[maxnode]; int fail[maxnode]; int Q[maxnode]; int ID[128]; int sz; int dp[2][maxnode][1<<10]; void init() { fail[0]=0; for(int i=0;i<childnum;i++) { ID[i+'a']=i; } } 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=0;i<childnum;i++) { if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } } while(s!=e) { int u=*s++; for(int i=0;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 word(int n,int m,int k) { memset(dp,0,sizeof(dp)); dp[0][0][0]=1; int s=0; for(int t=1;t<=n;t++) { int l=1-s; memset(dp[l],0,sizeof(dp[l])); for(int i=0;i<sz;i++) { for(int j=0;j<m;j++) { if(dp[s][i][j]) { for(int k=0;k<childnum;k++) { int p=chd[i][k]; int st=(j|val[p]); dp[l][p][st]+=dp[s][i][j]; if(dp[l][p][st]>=mod)dp[l][p][st]-=mod; } } } } s=l; } int ret=0; for(int j=0;j<m;j++) { int cnt=0; int st=j; while(st) { cnt+=(st&1); st>>=1; } if(cnt<k)continue; for(int i=0;i<sz;i++) { ret+=dp[s][i][j]; if(ret>=mod)ret-=mod; } } return ret; } }AC; int main() { AC.init(); int n,m,k; while(~scanf("%d%d%d",&n,&m,&k)) { if(!(n||m||k))break; AC.reset(); for(int i=1;i<=m;i++) { char temp[55]; scanf("%s",temp); AC.insert(temp,(1<<(i-1))); } AC.ac_build(); printf("%d\n",AC.word(n,1<<m,k)); } return 0; }