这题题意就是给你一个矩形的文本串,一个矩形的模式串,问你文本串中出现模式串的次数。
以前做过一维的情况,二维的话,就是增加一个count[][]数组,count[r][c]表示以(r,c)这个点为左上角的点,开始匹配,有多少行与模式串相同。如果相同的行数等于匹配矩形串的行数,则有加一。
如果文本串(x*y)的r行,第c列与模式串(n*m)的第row行匹配成功,则count[r-row+1][c-m+1]++;
构建AC自动机,就直接用矩形模式串的每行作为一个小的模式串构建。
还有一点要注意:因为矩形模式串中可能有很多行相同,所以要用一个链表保存行号。
小结:
AC自动机的初始化,最开始值更新根节点就可以了,其他点要等到访问的时候才更新。
对于AC自动机,还是用很多地方理解的不透彻,以至于修改和debug都很困难,还是得多做些题练手呀~~~
代码:
#include<iostream> #include<cstdio> #include<vector> #include<string> #include<map> #include<queue> #include<cmath> #include<algorithm> #include<cstring> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define maxn 10005 #define INF 0xfffffff #define mem(a,b) memset(a,b,sizeof(a)) #define FOR(i,s,t) for(int i=s;i<=t;i++) #define ull unsigned long long #define ll long long #define N 10005 #define M 1006 using namespace std; int n,m; int Cout[1005][1005]; struct node { int row,nxt; }edg[M*M]; int idx; struct AC { int ch[N][26],f[N],last[N]; int val[N]; int top; void init()//基本都得置0 { top=0; idx=0; //只初始化根节点 memset(ch[0],0,sizeof(ch[0])); f[0]=last[0]=0;val[0]=-1; } int NewNode() { int x=++top; memset(ch[x],0,sizeof(ch[x])); f[x]=last[x]=0; val[x]=-1; return x; } void AddEge(int x,int row) { edg[idx].row=row; edg[idx].nxt=val[x]; val[x]=idx++; } void insert(char *s,int num)//传参串的编号,类似trie的构建 { int l=strlen(s); int p=0; for(int i=0; i<l; i++) { int c=s[i]-'a'; if(ch[p][c]==0) { ch[p][c]=NewNode(); } p=ch[p][c]; } AddEge(p,num); } void getfail()//求每个结点的f数组和last数组,分别表示失配指针和失配的模式串指针 { queue<int> q; for(int c=0; c<26; c++)//初始化所有只有一个字符的节点的失配指针都指向树根 { int u=ch[0][c]; if(u) { q.push(u); } } while(!q.empty()) { int r=q.front(); q.pop(); for(int c=0; c<26; c++) { int u=ch[r][c]; if(!u)//如果r没有c这个子孙,那么就连到与r的失配指针指向的节点的c子孙 { ch[r][c]=ch[f[r]][c]; continue; } q.push(u);//如果该节点存在,更新f和last数组 int v=f[r]; while(v&&!ch[v][c]) v=f[v];//更新f f[u]=ch[v][c]; last[u]=val[f[u]]!=-1?f[u]:last[f[u]];//更新last } } } void find(char *s,int r)//记录行号 { int n=strlen(s); int p=0; for(int i=0; i<n; i++) { int c=s[i]-'a',col=i+1; p=ch[p][c];//这里直接匹配,可能匹配成功,也可能失败 if(val[p]!=-1) count(r,p,col);//如果匹配成功,并且有这个串,就计数 else if(last[p])//如果改点不是模式串,那就看他的失配指针 { count(r,last[p],col); } } } void count(int r,int x,int c) { if(x)//如果改点是模式串就计数,并且把有相同后记的所有节点递归计数 { //有多中模式串相同的情况 for(int i=val[x];i!=-1;i=edg[i].nxt) { int row=edg[i].row; if((r-row+1>=1)&&(c-m+1)>=1) { Cout[r-row+1][c+1-m]++;//r,i val[x],m } } if(last[x]) { count(r,last[x],c); } } } } ac; #define len 105 char str1[M][M],str2[len][len]; int main() { int t; scanf("%d",&t); while(t--) { int x,y; scanf("%d%d",&x,&y);//文本 for(int i=0;i<x;i++)//文本串 { scanf("%s",str1[i]); } ac.init(); scanf("%d%d",&n,&m);//模式 for(int i=0;i<n;i++)//模式串 { scanf("%s",str2[i]); ac.insert(str2[i],i+1); } ac.getfail(); memset(Cout,0,sizeof(Cout)); for(int i=0;i<x;i++) { ac.find(str1[i],i+1); } int ans=0; for(int i=1;i<=x;i++) { for(int j=1;j<=y;j++) { if(Cout[i][j]==n) ans++; } } printf("%d\n",ans); } return 0; }