UVa 11019 - Matrix Matcher 【二维矩阵匹配_AC自动机】

本文介绍了一种使用AC自动机和Trie树的高效算法,用于在大型字符矩阵中查找特定的子矩阵模式。通过构建模式矩阵的Trie树并利用AC自动机进行模式匹配,可以快速定位所有出现的模式实例。

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

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 characterseach.


Output


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


Sample Input

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


Sample Output

 

0
2


题意大意:从n*m的字符矩阵中找到有多少个x*y的字符矩阵。


思路:

我们对模式矩阵的每一行建trie树。 

corner[x][y]表示,以坐标(x, y)为右上顶点大小和模式矩阵相等的矩阵中有多少行与模式矩阵对应相同。

 

 

对于文本矩阵,我们对每一行的文本串进行处理,如果遇到模式串的结尾字符就进行corner[ ][ ]的更新。

  • corner[ ][ ]更新时,我们需要考虑模式矩阵中是否有相同也就是重复的模式串。所以我用了一个vector[rt][ ]来存标号为rt的结尾结点对应的是第几行的模式串。比如vt[2][ ] = {1, 2}:就说明模式矩阵中的第一行和第二行相同啦~【因为要为了corner的比较容易更新,所以是字符矩阵是从标号1开始的啦~】

像是这样

ab
ab
cd

 

 

 


AC CODE 【附上更优的二维哈希解法】 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>

using namespace std;

const int maxN = 10004;
const int maxS = 1003;

struct AC_automat{
    int trie[maxN][26], tot;
    int fail[maxN], last[maxN];
    int edNum[maxN];
    vector<int>vt[maxN];
    int corner[maxS][maxS];

    void Clear(int rt)
    {
        for(int i = 0; i < 26; ++ i ) trie[rt][i] = 0;
        edNum[rt] = 0;
        vt[rt].clear();
    }

    void init()
    {
        Clear(0);
        tot = 0;
        memset(corner, 0, sizeof(corner));
    }

    void Insert(char *s, int p)
    {
        int rt = 0;
        for(int i = 0; s[i]; ++ i )
        {
            int id = s[i] - 'a';
            if(!trie[rt][id]) { trie[rt][id] = ++ tot; Clear(tot); }
            rt = trie[rt][id];
        }
        ++ edNum[rt];
        vt[rt].push_back(p);
    }

    void build()
    {
        queue<int>q;
        for(int i = 0; i < 26; ++ i )
            if(trie[0][i])
                q.push(trie[0][i]), fail[trie[0][i]] = last[trie[0][i]] = 0;
        while(!q.empty())
        {
            int rt = q.front(); q.pop();
            for(int i = 0; i < 26; ++ i )
            {
                int son = trie[rt][i];
                if(trie[rt][i])
                {
                    fail[trie[rt][i]] = trie[fail[rt]][i];
                    q.push(trie[rt][i]);
                    last[son] = edNum[fail[rt]] ? fail[rt] : last[fail[rt]];
                } else trie[rt][i] = trie[fail[rt]][i];
            }
        }
    }

    void getCorner(int nowH, int nowL, int ed)
    {
        int num = vt[ed].size();
        if(nowH)
        {
            for(int i = 0; i < num; ++ i )
            {
                if(nowH >= vt[ed][i])
                    ++ corner[nowH - vt[ed][i]][nowL];
            }
        }
    }

    void Find(char *t, int nowH)
    {
        int now = 0;
        for(int i = 0; t[i]; ++ i )
        {
            int id = t[i] - 'a';
            now = trie[now][id];
            if(edNum[now]) getCorner(nowH, i, now);
            else
            {
                int rt = now;
                while (last[rt]) getCorner(nowH, i, last[rt]), rt = last[rt];
            }
        }
    }

    void print(int n, int m, int x)
    {
        int ans = 0;
        for(int i = 0; i <= n; ++ i )
            for(int j = 0; j <= m; ++ j )
                if(corner[i][j] == x)
                    ++ ans;
        printf("%d\n", ans);
    }

}ac;

char str[maxS][maxS];
char t[102][102];

int main()
{
    int TAT; scanf("%d", &TAT);
    while(TAT -- )
    {
        int n, m; scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++ i )
            scanf("%s", str[i]);
        int x, y; scanf("%d%d", &x, &y);
        for(int i = 1; i <= x; ++ i )
            scanf("%s", t[i]);
        if(x <= n && m >= y)
        {
            ac.init();
            for(int i = 1; i <= x; ++ i )
                ac.Insert(t[i], i);
            ac.build();
            for(int i = 1; i <= n; ++ i )
                ac.Find(str[i], i);
            ac.print(n, m, x);
        } else printf("0\n");
    }
    return 0;
}

/*
10
3 3
abc
bcd
bce
2 2
bc
cd
 */

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值