KMP算法
poj3461 Oulipo
题目大意:模板题。
思路:模板题。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int f[10010],ans; char s1[10000],s2[1000000]; void prework() { int i,j,n; n=strlen(s1); f[0]=f[1]=0; for (i=1;i<n;++i) { j=f[i]; while(j&&s1[i]!=s1[j]) j=f[j]; f[i+1]=(s1[j]==s1[i]?j+1:0); } } void work() { int n,m,i,j; n=strlen(s1);m=strlen(s2); j=0; for (i=0;i<m;++i) { while(j&&s1[j]!=s2[i]) j=f[j]; if (s1[j]==s2[i]) ++j; if (j==n) ++ans; } } int main() { int i,n; scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%*c%s%*c%s",&s1,&s2); prework(); ans=0;work(); printf("%d\n",ans); } }
poj2752 Seek the Name, Seek the Fame
题目大意:求一个串前缀和后缀完全一样的所有长度。
思路:维护出f数组后,从最后一位循环一下,如果ch[j]==ch[f[j]],f[j]+1就是一个可行的长度,j=f[j],一直循环下去。突然发现f数组很神奇。其实我们能保证前f[j]+1个元素和以j为结尾的f[j]+1个元素一定是相等的。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; char ch[400000]; int f[400001]={0},ans[400001]={0},l; void prework() { int i,j; f[0]=f[1]=0; for (i=1;i<l;++i) { j=f[i]; while(j&&ch[i]!=ch[j]) j=f[j]; f[i+1]=(ch[i]==ch[j]?j+1:0); } } void work() { int j; j=l-1; while(j&&ch[j]==ch[f[j]]) { ans[++ans[0]]=f[j]+1; j=f[j]; } } int main() { int i,j,n; while(scanf("%s",&ch)==1) { ans[0]=0;l=strlen(ch); ans[++ans[0]]=l; prework(); work(); for (i=ans[0];i;--i) printf("%d ",ans[i]); printf("\n"); } }
poj2406 Power Strings
题目大意:子串重复出现次数最多且重复后恰好是原串的长度。
思路:如果能重复后恰是原串的长度,那么l%(l-f[l])==0,如果能==0,则答案就是l/(l-f[l]),否则答案为1。这里一定是l和f[l],否则会出很多奇怪的问题。。。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; char ch[1000000]; int f[1000001]={0},l; void prework() { int i,j; f[0]=f[1]=0; for (i=1;i<l;++i) { j=f[i]; while(j&&ch[i]!=ch[j]) j=f[j]; f[i+1]=(ch[i]==ch[j]?j+1:0); } } int main() { int i,j,ans; while(scanf("%s",&ch)==1) { l=strlen(ch); if (ch[0]=='.') break; prework(); if (l%(l-f[l])==0) ans=l/(l-f[l]); else ans=1; printf("%d\n",ans); } }
poj1961 Period
题目大意:找出字符串中前缀长度为i(2<=i<=n)的字符串能被某个字符串重复k(k>1)遍得到,则输出这个i和k,按i的升序输出。
思路:其实就上上一题的小小加强版。循环一下i就可以了,从2到n,如果i%(i-f[i])==0则符合,k为i/(i-f[i])。其实不难理解,因为i-f[i]就是错位部分的长度,而之前的每一个这么长的部分都是相等的(如果是%==0的话)。这里有一点就是k>1,所以f[i]>0才能符合。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; char ch[1000000]; int f[1000001]={0},n; void prework() { int i,j; f[0]=f[1]=0; for (i=1;i<n;++i) { j=f[i]; while(j&&ch[j]!=ch[i]) j=f[j]; f[i+1]=(ch[i]==ch[j]?j+1:0); } } int main() { int i,j,kk=0; while(scanf("%d",&n)==1) { if (!n) break; scanf("%s",&ch); i=0;prework(); printf("Test case #%d\n",++kk); for (i=2;i<=n;++i) if (f[i]&&i%(i-f[i])==0) printf("%d %d\n",i,i/(i-f[i])); printf("\n"); } }
AC自动机
cogs1913
题目大意:模板题(模板无重复)。
思路:模板题。
#include<iostream> #include<cstdio> #include<cstring> #define sigma_size 26 #define maxnode 100000 #define len 100000 using namespace std; struct Trie{ int ch[maxnode][sigma_size],val[maxnode],last[maxnode],f[maxnode],sz,num,cnt[100]; void init() { sz=num=0; memset(ch[0],0,sizeof(ch[0])); memset(cnt,0,sizeof(cnt)); } int idx(char ch){return ch-'a';} void insert(char *s) { int u=0,n=strlen(s),i; for (i=0;i<n;++i) { int c=idx(s[i]); if (!ch[u][c]) { memset(ch[++sz],0,sizeof(ch[sz])); val[sz]=0;ch[u][c]=sz; } u=ch[u][c]; } val[u]=++num; } void prework() { int que[100001]={0},head,tail,u,r,v,i; f[0]=0;head=tail=0; for (i=0;i<sigma_size;++i) { u=ch[0][i]; if (u) { que[++tail]=u;f[u]=last[u]=0; } } while(head<tail) { r=que[++head]; for (i=0;i<sigma_size;++i) { u=ch[r][i]; if (!u){ch[r][i]=ch[f[r]][i];continue;} que[++tail]=u; v=f[r]; while(v&&!ch[v][i]) v=f[v]; f[u]=ch[v][i]; last[u]=val[f[u]]?f[u]:last[f[u]]; } } } void print(int j) { if (j) { ++cnt[val[j]]; print(last[j]); } } void find(char *s) { int u,n,i,j=0; n=strlen(s); for (i=0;i<n;++i) { u=idx(s[i]); j=ch[j][u]; if (val[j]) print(j); else if (last[j]) print(last[j]); } } }ac; char sta[51][50],ss[10000000]; int main() { freopen("ACautomata.in","r",stdin); freopen("ACautomata.out","w",stdout); int n,i,j; scanf("%d",&n); ac.init(); for (i=1;i<=n;++i) { scanf("%*c%s",&sta[i]); ac.insert(sta[i]); } ac.prework(); scanf("%*c%s",&ss); ac.find(ss); for (i=1;i<=n;++i) printf("%s %d\n",sta[i],ac.cnt[i]); fclose(stdin); fclose(stdout); }
poj1204Word Puzzles
题目大意:给一个字母矩阵,然后给定一些模板,找这些模板在矩阵中的位置,这些模板可以在矩阵中八个方向排列。
思路:裸的ac自动机,但是find的时候要八个方向。做的时候把模板倒着建的,这样找到匹配就是起始位置了,然后方向什么的就随便搞一下。
注意不要在结构体里开太大的数组,不然会很愉悦的RE。。。在很奇怪的地方RE。。。
#include<iostream> #include<cstdio> #include<cstring> #define sigma_size 26 #define maxnode 1000000 using namespace std; int l,c,w,que[1000001]={0}; char ss[1000][1000],ch[1001][1000]; struct Trie{ int ch[maxnode][sigma_size],f[maxnode],last[maxnode],val[maxnode],sz,num,cnt[1001][3]; void init() { sz=num=0; memset(ch[0],0,sizeof(ch[0])); memset(cnt,0,sizeof(cnt)); } int idx(char ch){return ch-'A';} void insert(char *s) { int n,i,u=0,c; n=strlen(s); for (i=n-1;i>=0;--i) { c=idx(s[i]); if (!ch[u][c]) { memset(ch[++sz],0,sizeof(ch[sz])); val[sz]=0;ch[u][c]=sz; } u=ch[u][c]; } val[u]=++num; } void prework() { int i,j,u,r,v,head,tail; f[0]=0;head=tail=0; for (i=0;i<sigma_size;++i) { u=ch[0][i]; if (u) { que[++tail]=u;f[u]=last[u]=0; } } while(head<tail) { r=que[++head]; for (i=0;i<sigma_size;++i) { u=ch[r][i]; if (!u){ch[r][i]=ch[f[r]][i];continue;} que[++tail]=u;v=f[r]; while(v&&!ch[v][i]) v=f[v]; f[u]=ch[v][i]; last[u]=(val[f[u]]?f[u]:last[f[u]]); } } } void print(int j,int x,int y,int kk) { if (j) { cnt[val[j]][0]=x;cnt[val[j]][1]=y; cnt[val[j]][2]=kk;print(last[j],x,y,kk); } } void find() { int i,j,k,u,v; for (i=0;i<l;++i) { u=0; for (j=0;j<c;++j) { v=idx(ss[i][j]); u=ch[u][v]; if (val[u]) print(u,i,j,6); else if (last[u]) print(last[u],i,j,6); } } for (i=0;i<l;++i) { u=0; for (j=c-1;j>=0;--j) { v=idx(ss[i][j]); u=ch[u][v]; if (val[u]) print(u,i,j,2); else if (last[u]) print(last[u],i,j,2); } } for (j=0;j<c;++j) { u=0; for (i=0;i<l;++i) { v=idx(ss[i][j]); u=ch[u][v]; if (val[u]) print(u,i,j,0); else if (last[u]) print(last[u],i,j,0); } } for (j=0;j<c;++j) { u=0; for (i=l-1;i>=0;--i) { v=idx(ss[i][j]); u=ch[u][v]; if (val[u]) print(u,i,j,4); else if (last[u]) print(last[u],i,j,4); } } for (j=1;j<=l+c-1;++j) { u=0;i=min(j,l)-1;k=j-i-1; while(i>=0&&k<c) { v=idx(ss[i][k]); u=ch[u][v]; if (val[u]) print(u,i,k,5); else if (last[u]) print(last[u],i,k,5); --i;++k; } } for (j=1;j<=l+c-1;++j) { u=0;k=min(c,j)-1;i=j-k-1; while(i<l&&k>=0) { v=idx(ss[i][k]); u=ch[u][v]; if (val[u]) print(u,i,k,1); else if (last[u]) print(last[u],i,k,1); ++i;--k; } } for (j=1;j<=l+c-1;++j) { u=0;i=min(l,j)-1;k=i-j+c; while(i>=0&&k>=0) { v=idx(ss[i][k]); u=ch[u][v]; if (val[u]) print(u,i,k,3); else if (last[u]) print(last[u],i,k,3); --i;--k; } } for (j=1;j<=l+c-1;++j) { u=0;i=max(1,j+1-c)-1;k=i-j+c; while(i<l&&k<c) { v=idx(ss[i][k]); u=ch[u][v]; if (val[u]) print(u,i,k,7); else if (last[u]) print(last[u],i,k,7); ++i;++k; } } } }ac; int main() { int i,j; scanf("%d%d%d",&l,&c,&w); for (i=0;i<l;++i) scanf("%*c%s",&ss[i]); for (i=1;i<=w;++i) { scanf("%*c%s",&ch[i]); ac.insert(ch[i]); } ac.prework(); ac.find(); for (i=1;i<=w;++i) printf("%d %d %c\n",ac.cnt[i][0],ac.cnt[i][1],ac.cnt[i][2]+'A'); }
cogs1376||bzoj2434 阿狸的打字机
题目大意:给定一个序列(由小写字母和B、P组成),如果是P就将有的字母组成一个新的字符串,如果是B就删掉栈顶的字母。查询给定x、y,查第x个字符串在y中出现的次数。
思路:自己写的话应该只能拿最暴力的40分。这里要求对ac自动机里失配边十分了解。我们边做便建立了一个ac自动机。考虑暴力的做法:从y的每一个节点不断root,如果能到x的结尾点就+1。在失配树(失配边的反向边组成的树)里,就是统计x的子树里有多少个y的节点。这样我们建好一颗失配树,做dfs序,在扫一遍给出的串,像一开始那样,不断的走,如果是结点的话,就给树状数组中的这个位置+1;如果P就看查询里y(排序,把y都搞到一起)为当前字串的,对x(之前记录下每种串的节点位置)进行查询;如果是B就把这个位置-1。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxnode 1000010 #define sigma_size 26 using namespace std; int que[maxnode]={0},tot=0,tt=0,tim[maxnode][2]={0},cc[maxnode]={0},ans[maxnode]={0},next[maxnode]={0},point[maxnode]={0},sz=0; struct use{ int st,en; }edge[maxnode]; struct used{ int xi,yi,po; }ask[maxnode]; struct Trie{ int ch[maxnode][sigma_size],val[maxnode],last[maxnode],f[maxnode],fa[maxnode],num,po[maxnode]; int lowbit(int x){return x&(-x);} void bins(int x,int xx) { for (;x<=tt;x+=lowbit(x)) cc[x]+=xx; } int bask(int l,int r) { int sum=0; --l; for (;r;r-=lowbit(r)) sum+=cc[r]; for (;l;l-=lowbit(l)) sum-=cc[l]; return sum; } void init() { sz=num=0; memset(ch[0],0,sizeof(ch[0])); } int idx(char ch){return ch-'a';} void insert(char *s) { int n,i,j,u=0,v; n=strlen(s);fa[0]=0; for (i=0;i<n;++i) { if (s[i]>='a'&&s[i]<='z') { v=idx(s[i]); if (!ch[u][v]) { ch[u][v]=++sz;val[sz]=0;fa[sz]=u; memset(ch[sz],0,sizeof(ch[sz])); } u=ch[u][v]; } if (s[i]=='P') { val[u]=++num;po[num]=u; } if (s[i]=='B') u=fa[u]; } } void prework() { int n,i,j,u,v,r,head,tail; head=tail=0;f[0]=0; for (i=0;i<sigma_size;++i) { u=ch[0][i]; if (u) { que[++tail]=u;last[u]=f[u]=0; } } while(head<tail) { r=que[++head]; for (i=0;i<sigma_size;++i) { u=ch[r][i]; if (!u){ch[r][i]=ch[f[r]][i];continue;} que[++tail]=u;v=f[r]; while(v&&!ch[v][i]) v=f[v]; f[u]=ch[v][i]; last[u]=(val[f[u]]?f[u]:last[f[u]]); } } } void work(char *s,int up) { int n,i,j,u=0,now=1; n=strlen(s); for (i=0;i<n;++i) { if (s[i]>='a'&&s[i]<='z') { u=ch[u][idx(s[i])];bins(tim[u][0],1); } if (s[i]=='B') { bins(tim[u][0],-1);u=fa[u]; } if (s[i]=='P') { while (val[u]==ask[now].yi&&now<=up) { ans[ask[now].po]=bask(tim[po[ask[now].xi]][0],tim[po[ask[now].xi]][1]); ++now; } } } } }ac; char ch[maxnode]; bool visit[maxnode]={false}; int my_comp(const used &x,const used &y) { if (x.yi<y.yi) return 1; else return 0; } void add(int st,int en) { ++tot;next[tot]=point[st];point[st]=tot; edge[tot].st=st;edge[tot].en=en; } void build(int u) { int i; visit[u]=true;tim[u][0]=++tt; for (i=point[u];i;i=next[i]) if (!visit[edge[i].en]) build(edge[i].en); tim[u][1]=tt; } int main() { freopen("noi2011_type.in","r",stdin); freopen("noi2011_type.out","w",stdout); int i,j,n; scanf("%s",&ch); ac.insert(ch); ac.prework(); for (i=1;i<=sz;++i) add(ac.f[i],i); build(0); scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%d%d",&ask[i].xi,&ask[i].yi); ask[i].po=i; } sort(ask+1,ask+n+1,my_comp); ac.work(ch,n); for (i=1;i<=n;++i) printf("%d\n",ans[i]); fclose(stdin); fclose(stdout); }
bzoj1468 文本生成器
题目大意:给定N个模板串,求长度为M的串中出现模板串的个数。
思路:建立一个ac自动机,标记一下每个单词的结尾,然后dp一下,用记忆化搜索dfs(u(当前结点),m(本次未选时剩余长度))实现,如果是单词结尾就给答案加上26^(m-1),否则就dfs下去。
#include<iostream> #include<cstdio> #include<cstring> #define sigma_size 26 #define maxnode 10000 #define P 10007 #define len 110 using namespace std; int mi[len]={0},que[maxnode]={0},dp[maxnode][len]; char ss[len]; struct Trie{ int ch[maxnode][sigma_size],val[maxnode],f[maxnode],sz,num; int idx(char u){return u-'A';} void init() { sz=num=0; memset(ch[0],0,sizeof(ch[0])); } void insert(char *s) { int n,i,j,u=0,v; n=strlen(s);val[0]=0; for (i=0;i<n;++i) { v=idx(s[i]); if (!ch[u][v]) { ch[u][v]=++sz;val[sz]=0; memset(ch[sz],0,sizeof(ch[sz])); } u=ch[u][v]; } val[u]=1; } void prework() { int i,j,u,v,r,head,tail; head=tail=0;f[0]=0; for (i=0;i<sigma_size;++i) { u=ch[0][i]; if (u) { que[++tail]=u;f[u]=0; } } while(head<tail) { r=que[++head]; for (i=0;i<sigma_size;++i) { u=ch[r][i]; if (!u) { ch[r][i]=ch[f[r]][i];continue; } que[++tail]=u;v=f[r]; while(v&&!ch[v][i]) v=f[v]; f[u]=ch[v][i]; val[u]|=val[f[u]]; } } } int dfs(int u,int m) { int i,j,ans=0; if (!m) return 0; if (dp[u][m]>=0) return dp[u][m]; for (i=0;i<sigma_size;++i) { j=ch[u][i]; if (!val[j]) ans=(ans+dfs(j,m-1))%P; else ans=(ans+mi[m-1])%P; } dp[u][m]=ans; return ans; } }ac; int main() { int n,m,i,j,ans; for (i=0;i<maxnode;++i) for (j=0;j<len;++j) dp[i][j]=-1; scanf("%d%d",&n,&m); mi[0]=1; for (i=1;i<=m;++i) mi[i]=(mi[i-1]*26)%P; ac.init(); for (i=1;i<=n;++i) { scanf("%*c%s",&ss); ac.insert(ss); } ac.prework(); ans=ac.dfs(0,m)%P; printf("%d\n",ans); }
cogs1468 文本生成器
题目大意:同上。
思路:这道题目中m的长度<=10^6,所以如果还是dfs的话,就炸掉了。后来得知是矩阵乘法。我们根据trie建一个矩阵mat[][],mat[i][j]表示i到j走1步并且不碰到单词结点的种数,矩阵乘一下,然后我们发现它的m次方中mat[0][i]的和就是所有不包含单词的个数,用总的个数减去这个就可以了。这样,我们就可以处理很多这种文章很长但是模板很小的ac自动机上的dp了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define sigma_size 26 #define maxnode 65 #define P 10007 #define len 1000010 using namespace std; struct matr{ int num[maxnode][maxnode]; }mat,m1,m2; int mi[len]={0},que[maxnode]={0}; char ss[len]; struct Trie{ int ch[maxnode][sigma_size],val[maxnode],f[maxnode],sz,num; int idx(char u){return u-'A';} void init() { sz=num=0; memset(ch[0],0,sizeof(ch[0])); } void insert(char *s) { int n,i,j,u=0,v; n=strlen(s);val[0]=0; for (i=0;i<n;++i) { v=idx(s[i]); if (!ch[u][v]) { ch[u][v]=++sz;val[sz]=0; memset(ch[sz],0,sizeof(ch[sz])); } u=ch[u][v]; } val[u]=1; } void prework() { int i,j,u,v,r,head,tail; head=tail=0;f[0]=0; for (i=0;i<sigma_size;++i) { u=ch[0][i]; if (u) { que[++tail]=u;f[u]=0; } } while(head<tail) { r=que[++head]; for (i=0;i<sigma_size;++i) { u=ch[r][i]; if (!u) { ch[r][i]=ch[f[r]][i];continue; } que[++tail]=u;v=f[r]; while(v&&!ch[v][i]) v=f[v]; f[u]=ch[v][i]; val[u]|=val[f[u]]; } } } void prematrix() { int i,j,u; for (i=0;i<=sz;++i) for (j=0;j<=sz;++j) mat.num[i][j]=0; for (i=0;i<=sz;++i) for (j=0;j<sigma_size;++j) { u=ch[i][j]; if (!val[i]&&!val[u]) ++mat.num[i][u]; } } void matrixmult(matr mu1,matr mu2,matr &mu3) { int i,j,k; for (i=0;i<=sz;++i) for (j=0;j<=sz;++j) { mu3.num[i][j]=0; for (k=0;k<=sz;++k) mu3.num[i][j]=(mu3.num[i][j]+mu1.num[i][k]*mu2.num[k][j])%P; } } void matrix(int m) { int i,j; if (m==1) { for (i=0;i<=sz;++i) for (j=0;j<=sz;++j) m1.num[i][j]=mat.num[i][j]; return; } matrix(m/2); matrixmult(m1,m1,m2); if (m%2) matrixmult(m2,mat,m1); else swap(m1,m2); } }ac; int main() { freopen("textgen.in","r",stdin); freopen("textgen.out","w",stdout); int n,m,i,j,ans=0; scanf("%d%d",&n,&m); mi[0]=1; for (i=1;i<=m;++i) mi[i]=(mi[i-1]*26)%P; ac.init(); for (i=1;i<=n;++i) { scanf("%*c%s",&ss); ac.insert(ss); } ac.prework(); ac.prematrix(); ac.matrix(m); for (i=0;i<=ac.sz;++i) ans=(ans+m1.num[0][i])%P; ans=(mi[m]-ans+P)%P; printf("%d\n",ans); fclose(stdin); fclose(stdout); }
poj2778 DNA Sequence
题目大意:给定n个DNA序列,求长度为m的不包含这些给定的DNA序列的种类数。(m<=2000000000)。
思路:和上一个题一样,面对这么大的m,我们还是要用到矩阵。这里矩阵里∑mat[0][i]就是答案了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxnode 110 #define P 100000 #define sigma_size 4 using namespace std; int que[maxnode]={0}; char ss[maxnode]; struct matr{ long long num[maxnode][maxnode]; }mat,m1,m2; struct Trie{ int ch[maxnode][sigma_size],val[maxnode],f[maxnode],sz,num; int idx(char s) { if (s=='A') return 0; if (s=='C') return 1; if (s=='G') return 2; if (s=='T') return 3; } void init() { sz=num=0; memset(ch[0],0,sizeof(ch[0])); } void insert(char *s) { int n,i,j,u=0,v; n=strlen(s); for (i=0;i<n;++i) { v=idx(s[i]); if (!ch[u][v]) { ch[u][v]=++sz;val[sz]=0; memset(ch[sz],0,sizeof(ch[sz])); } u=ch[u][v]; } val[u]=1; } void prework() { int n,i,j,u,r,v,head,tail; head=tail=0;u=0;f[u]=0; for (i=0;i<sigma_size;++i) { u=ch[0][i]; if (u) { que[++tail]=u;f[u]=0; } } while(head<tail) { r=que[++head]; for (i=0;i<sigma_size;++i) { u=ch[r][i]; if (!u) { ch[r][i]=ch[f[r]][i];continue; } que[++tail]=u;v=f[r]; while(v&&!ch[v][i]) v=f[v]; f[u]=ch[v][i]; val[u]|=val[f[u]]; } } } void prematrix() { int i,j,u; for (i=0;i<=sz;++i) for (j=0;j<=sz;++j) mat.num[i][j]=0; for (i=0;i<=sz;++i) for (j=0;j<sigma_size;++j) { u=ch[i][j]; if (!val[u]&&!val[i]) ++mat.num[i][u]; } } void matrixmult(matr mu1,matr mu2,matr &mu3) { int i,j,k; for (i=0;i<=sz;++i) for (j=0;j<=sz;++j) { mu3.num[i][j]=0; for (k=0;k<=sz;++k) mu3.num[i][j]=(mu3.num[i][j]+mu1.num[i][k]*mu2.num[k][j])%P; } } void matrix(int m) { int i,j; if (m==1) { for (i=0;i<=sz;++i) for (j=0;j<=sz;++j) m1.num[i][j]=mat.num[i][j]; return; } matrix(m/2); matrixmult(m1,m1,m2); if (m%2) matrixmult(m2,mat,m1); else swap(m1,m2); } }ac; int main() { int i,j,n,m,ans=0; scanf("%d%d",&n,&m); ac.init(); for (i=1;i<=n;++i) { scanf("%*c%s",&ss); ac.insert(ss); } ac.prework(); ac.prematrix(); ac.matrix(m); for (i=0;i<=ac.sz;++i) ans=(int)((long long)ans+m1.num[0][i])%P; printf("%d\n",ans); }
poj1625 Censored!
题目大意:同上。
思路:因为没有mod,所以高精度一下。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxnode 10000 using namespace std; char ha[50],ss[20]; int n,que[maxnode]={0}; struct use{ int l,num[500]; }dp[maxnode][51]; bool vis[maxnode][51]={false}; struct use jia(struct use a,struct use b) { struct use c; int i,j; j=0;c.l=max(a.l,b.l); memset(c.num,0,sizeof(c.num)); for (i=1;i<=c.l;++i) { c.num[i]=j+a.num[i]+b.num[i]; j=c.num[i]/10; c.num[i]%=10; } if (j) c.num[++c.l]=j; return c; } struct Trie{ int ch[maxnode][50],f[maxnode],val[maxnode],sz,num; void init() { sz=num=0;val[0]=0; memset(ch[0],0,sizeof(ch[0])); } int idx(char ss) { int l,r,mid; l=0;r=n-1; while(l!=r) { mid=(l+r)/2; if (ss<=ha[mid]) r=mid; else l=mid+1; } return l; } void insert(char *s) { int l,i,j,u=0,v; l=strlen(s); for (i=0;i<l;++i) { v=idx(s[i]); if (!ch[u][v]) { ch[u][v]=++sz;val[sz]=0; memset(ch[sz],0,sizeof(ch[sz])); } u=ch[u][v]; } val[u]=1; } void prework() { int head,tail,i,j,r,u,v; f[0]=0;head=tail=0; for (i=0;i<n;++i) { u=ch[0][i]; if (u) { que[++tail]=u;f[u]=0; } } while(head<tail) { r=que[++head]; for (i=0;i<n;++i) { u=ch[r][i]; if (!u) { ch[r][i]=ch[f[r]][i];continue; } que[++tail]=u;v=f[r]; while(v&&!ch[v][i]) v=f[v]; f[u]=ch[v][i]; val[u]|=val[f[u]]; } } } }ac; struct use work(int u,int m) { struct use c; int i,j,v; c.l=1;memset(c.num,0,sizeof(c.num)); c.num[1]=1; if (!m) return c; if (vis[u][m]) return dp[u][m]; vis[u][m]=true;c.num[1]=0; for (i=0;i<n;++i) { v=ac.ch[u][i]; if (!ac.val[v]) c=jia(c,work(v,m-1)); } dp[u][m]=c; return c; } int main() { int m,p,i,j; scanf("%d%d%d%*c",&n,&m,&p); gets(ha); sort(ha+0,ha+n); ac.init(); for (i=1;i<=p;++i) { scanf("%s%*c",&ss); ac.insert(ss); } ac.prework(); use ans; ans=work(0,m); for (i=ans.l;i;--i) printf("%d",ans.num[i]); printf("\n"); }
ZOJ3228 Searching the String
题目大意:很多模板串,在文本中找这些模板串出现的次数(可以重复和不可以重复)。
思路:我们保存一下每一种串上一次在文本串中的位置,如果不重复就+1。因为这一题模板串可以重复,一开始用next数组把一个节点对应的所有模板串都加进去了,然后tle的很惨,后来发现一个点对应的模板串唯一,只是有两种状态(即两种查询),所以用个数组保存一下,最后输出的时候对应过去就可以了。
#include<iostream> #include<cstdio> #include<cstring> #define sigma_size 26 #define maxnode 600010 #define len 100001 using namespace std; int kk[len]={0},que[maxnode]={0},ll[len]={0}; char ss[len]; struct Trie{ int ch[maxnode][sigma_size],f[maxnode],val[maxnode],last[maxnode],sz,num,cnt[maxnode][3],next[len],posi[len]; bool uu[maxnode][2]; int idx(char s){return s-'a';} void init() { int i; memset(ch[0],0,sizeof(ch[0])); for (i=0;i<maxnode;++i) { cnt[i][0]=0;cnt[i][1]=0;cnt[i][2]=-1; } memset(uu,false,sizeof(uu)); sz=num=0;val[0]=0; } void insert(char *s) { int n,i,j,u=0,v; n=strlen(s); for (i=0;i<n;++i) { v=idx(s[i]); if (!ch[u][v]) { ch[u][v]=++sz;val[sz]=0; memset(ch[sz],0,sizeof(ch[sz])); } u=ch[u][v]; } val[u]=++num;ll[num]=n;uu[u][kk[num]]=true;posi[num]=u; } void prework() { int i,j,u,v,r,head,tail; f[0]=0;head=tail=0; for (i=0;i<sigma_size;++i) { u=ch[0][i]; if (u) { que[++tail]=u;f[u]=last[u]=0; } } while(head<tail) { r=que[++head]; for (i=0;i<sigma_size;++i) { u=ch[r][i]; if (!u) {ch[r][i]=ch[f[r]][i];continue;} que[++tail]=u;v=f[r]; while(v&&!ch[v][i]) v=f[v]; f[u]=ch[v][i]; last[u]=(val[f[u]]?f[u]:last[f[u]]); } } } void print(int i,int j) { int k; if (j) { if (uu[j][0]) ++cnt[j][0]; if (uu[j][1]&&cnt[j][2]+ll[val[j]]<=i) { ++cnt[j][1];cnt[j][2]=i; } print(i,last[j]); } } void find(char *s) { int n,i,j,u=0; n=strlen(s); for (i=0;i<n;++i) { j=idx(s[i]); u=ch[u][j]; if (val[u]) print(i,u); else if (last[u]) print(i,last[u]); } } }ac; int main() { int n,i,j=0; char s[10]; while(scanf("%s",&ss)==1) { scanf("%d",&n);++j; ac.init(); for (i=1;i<=n;++i) { scanf("%d%s",&kk[i],&s); ac.insert(s); } scanf("%*c"); ac.prework(); ac.find(ss); printf("Case %d\n",j); for (i=1;i<=n;++i) printf("%d\n",ac.cnt[ac.posi[i]][kk[i]]); printf("\n"); } }