UVA 11019 AC自动机-二维模式串匹配

OJ:https://cn.vjudge.net/problem/UVA-11019

简单翻译:

给你一个n*m的字符矩阵T,在给你一个x*y的字符矩阵P,问P在T中能完全匹配几个位置。

 

这个题我们利用AC自动机解决首先要解决两个问题:

1.要想P与T的的某个位置完全匹配,那么P的每一行要与T的对应的部分匹配,所以可以把P每一行看成模式串构造出AC自动机。

然后在T中每一行逐一匹配,找到P中的所有模式串,与T中的每一个匹配点。我们要记录一些东西,记录匹配到的串是从T当前行的哪个一列开始匹配,还有匹配到的模式串是哪一个。我们用count[r][c]表示T中以(r,c)为左上角,与P等大矩形中有多少个完整的行与P对应的位置行完全相同。那么如果P的第i行出现在T第r行出现匹配,匹配开始的列为c,意味着count[r-i][c]应该加1。最终count[r][c]=p的行数的那些位置,就是一个匹配点。

 

2.如果给定的模式串中存在重复的串,如果不对AC自动化机进行改造我们就不能得到想要的结果,因为我们在匹配的时候要记录当前匹配到的是哪一个模式串,但是有两个相同的,怎么办,我们该记录哪一个?因为匹配到的P中的哪一行不能确定。既然这样,我们匹配到某个存在重复的模式串之后,我们可以将这个重复的模式串在P中所有可能的行,对应的count[][]位置都进行加1操作,就解决了这个问题。因为他们匹配结果属于不同的小矩阵,所以对最终的结果没有影响

 

代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 10001
#define array_sizeof(T) ((size_t)(&T+1)  - (size_t)(&T))
char map[1001][1001];
char p[101][101];
// 记录小矩阵中匹配了多少对应的行
int match[1001][1001];


typedef struct{
    int size;
    int queue[10];
}Queue;

typedef struct{
    int val[MAX];
    // 使用一个队列来记录,如果当前节点是一个字符串结束位置,那么当前串对应的在P中所属的行号,因为可能存在重复,所以使用队列
    Queue queue[MAX];
    int word[MAX][26];
    int last[MAX];
    int fail[MAX];
    int size;
}Trie;


Trie trie;
int idx(char c);
void push(int data);
int pop();
int isEmpty();
void buildAc();
void insert(char *s,int index);
int queue[MAX],next,pre,size;

void buildAc(){
    int i;
    for(i=0;i<26;i++){
        int index = trie.word[0][i];
        if(index){
            push(index);
            trie.fail[index] = 0;
            trie.last[index] = 0;
        }
    }
    while(!isEmpty()){
        int parentNode = pop();
        for(i=0;i<26;i++){
            int index = trie.word[parentNode][i];
            if(index){
                push(index);
                int pFail = trie.fail[parentNode];
                while(pFail && !trie.word[pFail][i]){
                    pFail = trie.fail[pFail];
                }
                trie.fail[index] = trie.word[pFail][i];
                trie.last[index] = trie.val[trie.fail[index]] ? trie.fail[index] :
                    trie.last[trie.fail[index]];
            }
        }
    }
}

void insert(char *s,int index){
    int curNode=0,len=strlen(s);
    int i;
    for(i=0;i<len;i++){
        int index = idx(s[i]);
        if(!trie.word[curNode][index]){
            memset(trie.word[trie.size],0,sizeof(trie.word[0]));
            trie.val[trie.size] = 0;
            trie.word[curNode][index] = trie.size++;
        }
        curNode = trie.word[curNode][index];
    }
    if(!trie.val[curNode]){
        trie.val[curNode] = 1;
        trie.queue[curNode].size = 0;
    }
    trie.queue[curNode].queue[trie.queue[curNode].size++] = index;
}

void push(int data){
    size++;
    queue[next++] = data;
    if(next == MAX){
        next = 0;
    }
}

int pop(){
    size--;
    int res = queue[pre++];
    if(pre == MAX){
        pre = 0;
    }
    return res;
}

int isEmpty(){
    return size == 0;
}

int idx(char c){
    return c - 'a';
}

void find(int line,int index,int curNode){
    int k = 0;
    // 将重复的字符串,每个行对应的矩形都加1
    for(;k<trie.queue[curNode].size;k++){
        int matchStrIdx = trie.queue[curNode].queue[k];
        int matchStrLen = strlen(p[matchStrIdx]);
        if(line - matchStrIdx >= 0){
            match[line-matchStrIdx][index-matchStrLen+1]+=1;
        }
    }
}

void init(){
    trie.size=1;
    memset(trie.word[0],0,sizeof(trie.word[0]));
    memset(trie.val,0,sizeof(trie.val));
    memset(trie.last,0,sizeof(trie.last));
    int i=0;
    for(;i<1001;i++){
        memset(match[i],0,sizeof(match[i]));
    }
}

int main()
{
    int count = 0,i,j;
    scanf("%d",&count);
    for(;count>0;count--){
        init();
        int n,m,x,y;
        scanf("%d%d",&n,&m);
        for(i=0;i<n;i++){
            scanf("%s",map[i]);
        }
        scanf("%d%d",&x,&y);
        for(i=0;i<x;i++){
            scanf("%s",p[i]);
            insert(p[i],i);
        }
        buildAc();
        for(i=0;i<n;i++){
            int curNode = 0;
            for(j=0;j<m;j++){
                int nextNode = trie.word[curNode][idx(map[i][j])];
                if(nextNode){
                    if(trie.val[nextNode]){
                        find(i,j,nextNode);
                    }
                    int node = nextNode;
                    while(trie.last[node]){
                        node = trie.last[node];
                        find(i,j,node);
                    }
                    curNode = nextNode;
                }else{
                    curNode = trie.fail[curNode];
                    if(curNode!=0){
                        j--;
                    }
                }
            }
        }
        int sum = x;
        int res = 0;
        for(i=0;i<n;i++){
            for(j=0;j<m;j++){
                if(match[i][j] == sum){
                    res++;
                }
            }
        }
        printf("%d\n",res);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值