UVA11019 Matrix Matcher(一种计算子矩阵在母矩阵中出现次数的办法,AC自动机+kmp)

本文介绍了一种使用AC自动机和KMP算法解决矩阵匹配问题的方法,通过构建自动机并结合优化后的搜索策略,有效地计算出子矩阵在母矩阵中的出现次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Problem H
Matrix Matcher
Input: 
Standard Input

Output: Standard Output

 

Given an N * M matrix, your task is to find the number of occurences of an X * Y pattern.

 

Input

The first line contains a single integer t(t ≤ 15), the number of test cases.

 

For each case, the first line contains two integers N and M (N, M ≤ 1000). The next N lines contain M characters each.

 

The next line contains two integers X and Y (X, Y ≤ 100). The next X lines contain Y characters each. 

 

Output

For each case, output a single integer in its own line, the number of occurrences.

Sample Input     Output for Sample Input

2 
1 1
x 
1 1
y 
3 3
abc 
bcd
cde 
2 2
bc
cd

                                                               0

                                                               2

 



Problem Setter: Rujia Liu, EPS

Special Thanks: Wenbin Tang

 

Warming: The judge input file size is about 7 MB. So please make sure that you use a fast IO function (eg. scanf()) to read input.


题目地址:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1960

 

说白了,判断一个子矩阵在母矩阵中出现的次数,怎么办?

矩阵中的元素是字母,字母是在'a'-'z'之间的

暴力扫不现实,用AC自动机来做。先把子矩阵的每一行当作一个模版串,构成AC自动机

然后把母矩阵的每一行当作文本串,放到自动机里去扫。

边扫边构成一个新的矩阵。新矩阵的定义是 i,j 这个位置在母矩阵中,如果是一个模版串的尾字母,那就存储这个尾字母对应的节点编号。否则存储0.

在之前构建自动机的时候,可以记录每一行它尾字母对应的节点的编号,这就构成一个新的模版串

而新的矩阵的每一列又可以看做一个新的文本串

这样,对新的模版串和m个新的文本串跑m发kmp.. 就可以求出子矩阵在母矩阵出现的次数了。


其实,AC自动机是必要的,再用上kmp是我自己想出来的。相信还有别的方法算出子矩阵在母矩阵中出现的次数。不管怎样,我就我的傻逼方法了

这题很好,算是一道综合题吧。I like it.


//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi 3.1415926535898
#define eps 1e-6
#define MOD 1000000007
#define MAXN 1024
#define N 300100
#define M 26
char matrix[MAXN][MAXN];
char word[200];
int belong[200];
int n,m,x,y;
int num_node,trie[N][M],val[N];
int idx(char c){
    return c-'a';
}
void init_trie()
{
    clr(trie[0]);
    num_node=1;
}
void plant_trie(char *s,int len,int number)
{
    int now=0,letter;
    rep(i,0,len)
    {
        letter=idx(s[i]);
        if(!trie[now][letter])
        {
            trie[now][letter]=num_node;
            val[num_node]=0;
            clr(trie[num_node]);
            num_node+=1;
        }
        now=trie[now][letter];
    }
    val[now]=number;
    belong[number-1]=now;
}
int nextpos[N],lastpos[N];
queue<int>q;
void Build_nextlastpos()
{
    clr_queue(q);
    nextpos[0]=lastpos[0]=0;
    int now;
    rep(i,0,M)
    {
        now=trie[0][i];
        if(now)
        {
            nextpos[now]=0;
            lastpos[now]=0;
            q.push(now);
        }
    }
    int son,nextone;
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        rep(i,0,M)
        {
            son=trie[now][i];
            nextone=nextpos[now];
            if(!son)
            {
                trie[now][i]=trie[nextone][i];//AC自动机优化
                continue;
            }
            while(nextone && !trie[nextone][i]) nextone=nextpos[nextone];//AC自动机优化
            nextpos[son]=trie[nextone][i];
            lastpos[son]=val[nextpos[son]]?nextpos[son]:lastpos[nextpos[son]];
            q.push(son);
        }
    }
}
int newmatrix[MAXN][MAXN];
void print(int row,int col,int pos)
{
    if(pos)
    {
        newmatrix[row-1][col-1]=pos;
        print(row,col,lastpos[pos]);
    }
}
void Aho_Corasick_search(int row,char *s,int len)
{
    int now=0,letter;
    repin(i,1,len)
    {
        letter=idx(s[i]);
        now=trie[now][letter];
        if(val[now]) print(row,i,now);
        else if(lastpos[now]) print(row,i,lastpos[now]);
    }
}
//上面是AC自动机
//下面是KMP
int kmpnext[MAXN];
void Build_kmpnext()
{
    int i,j;
    i=0;
    kmpnext[0]=j=-1;
    while(i<x)
    {
        if(j==-1 || belong[i]==belong[j])
        {
            kmpnext[i+1]=j+1;
            if(belong[j+1]==belong[i+1]) kmpnext[i+1]=kmpnext[j+1];//kmp优化
            i++;
            j=j+1;
        }
        else j=kmpnext[j];
    }
}
ll ans;
void kmp_search(int col)
{
    int i,j;
    i=j=0;
    while(i<n)
    {
        if(j==-1 || newmatrix[i][col]==belong[j])
        {
            i++;
            j++;
        }
        else j=kmpnext[j];
        if(j==x)
        {
            ans+=1;
            j=kmpnext[j];
        }
    }
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d %d",&n,&m);
        repin(i,1,n)
        {
            repin(j,1,m)
            {
                newmatrix[i][j]=0;
            }
            scanf("%s",matrix[i]+1);
        }
        init_trie();
        scanf("%d %d",&x,&y);
        repin(i,1,x)
        {
            scanf("%s",word);
            plant_trie(word,y,i);
        }
        Build_nextlastpos();
        repin(i,1,n)
        {
            Aho_Corasick_search(i,matrix[i],m);
        }
        Build_kmpnext();
        ans=0;
        repin(j,1,m)
        {
            kmp_search(j);
        }
        printf("%lld\n",ans);
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值