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;
}