Uva11019 AC自动机解决矩形模式串与文本串的匹配问题

这题题意就是给你一个矩形的文本串,一个矩形的模式串,问你文本串中出现模式串的次数。

以前做过一维的情况,二维的话,就是增加一个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;
}



你可能感兴趣的:(Uva11019 AC自动机解决矩形模式串与文本串的匹配问题)