poj 1204 Word Puzzles AC自动机

http://poj.org/problem?id=1204

题意:给你一个N*M的由大写字符组成的矩阵,再给你M个长度不超过1000的字符串,要你确定这些字符串在大矩阵中的开始位置和方向。

思路:AC自动机,对匹配串建立Trie树和失败指针,然后就是进行匹配了,所不同的是这里给你模式串不一维的,因此匹配的时候会有一点不同,一开始错误地以为需要对每个点进行8个方向的枚举匹配,这样的复杂度就会达到O(8*N^3) = 8*10^9,这样就会超时,但是后来才发现原来并不需要对每个结点的8个方向都枚举,只只要枚举每个方向的开始的那个点就可以了,这样复杂度就是O(N^2),最多个常数因子,因此就可以解决本题了。另外还有一个技巧就是,因为最后需要输出每个匹配串在模式串中的开始位置, 我们这里可以将匹配串反向建树,就可以很简单地解决这个问题。

代码:

/*
AC自动机 
*/
#include<stdio.h>
#include<string.h>
int N , M , Q ;
char map[1010][1010] ;
char str[1010] ;
struct Node{
	int f ;				//标记是否结束 
	int num ;			//单词的编号 
	Node *fail ;
	Node *next[26] ;
	Node(){
		fail = 0 ; 
		f = 0 ;
		num = -1 ;
		memset(next,  0 , sizeof(next));
	}
}*root ;
Node *que[1000010] ;
int front , rear ;

void build_trie(char *ch,int num){
	int idx ,len;
	Node *loc = root , *q ;
	len = strlen(ch);
	for(int i=len-1;i>=0;i--){
		idx = ch[i] - 'A';
		if(loc->next[idx] == NULL){
			q = new Node ;
			loc->next[idx] = q ;
		}
		loc = loc->next[idx] ;
	}
	loc->f = 1 ;
	loc->num = num ;
}
void ac_automation(){
	Node *loc = root ,*temp;
	root->fail = NULL ;
	front = rear = 0;
	que[rear++] = root ;
	while(front < rear){
		loc = que[front++] ;
		for(int i=0;i<26;i++){
			if(loc->next[i] == NULL)	continue ;
			if(loc == root){
				loc->next[i]->fail = root ;
			}
			else{
				temp = loc->fail ;
				while(temp != NULL){
					if(temp->next[i] != NULL){
						loc->next[i]->fail = temp->next[i] ;
						break ;
					}
					temp = temp->fail ;	
				}
				if(temp == NULL){
					loc->next[i]->fail = root ;
				}
			}
			que[rear++] = loc->next[i] ;
		}
	}
}
char part[1010] ;
int dr[8] = {0,1,1,1,0,-1,-1,-1} ;
int dc[8] = {1,1,0,-1,-1,-1,0,1} ;
int ans_r[1010] ;
int ans_c[1010] ;
int ans_dir[1010] ;
bool vis[1010] ;

void query(int row ,int col, int dir){
	int i ,j ,idx ;
	Node *loc = root ,*q;
	int r = row ,c = col ;
	int nr , nc ;
	
	for( ; ; ){
		idx = map[r][c] - 'A' ;
		while(loc!=root && loc->next[idx]==NULL)	loc=loc->fail ;
		loc = loc->next[idx] ;
		if(loc == NULL)	loc = root ;
		q = loc ;
		while(q != root){
			if(q->f){
				int n = q->num;
				if(n!=-1 && vis[n]==0){
					ans_r[n] = r ;
					ans_c[n] = c ;
					ans_dir[n] = dir ;
					vis[n] = 1 ;		
				}	 
			}
			q = q->fail ;
		}
		r += dr[dir] ;
		c += dc[dir] ;
		if(r<0 || r>=N || c<0 || c>=M)	break ;
	}
}
char ddd[8] = {'C','D','E','F','G','H','A','B'};
/*
0	: C	  
1	: D
2	: E
3	: F
4	: G
5	: H
6	: A
7	: B
*/

void solve(){
	int i, j ;
	for(i=0;i<N;i++){
		query(i,0,0);			
		query(i,M-1,4);			
	}
	for(i=0;i<N;i++){
		query(i,0,1);		
		query(i,M-1,3);
	}
	for(j=0;j<M;j++){
		query(0,j,1);
		query(0,j,3);
	}
	for(j=0;j<M;j++){
		query(0,j,2);	
		query(N-1,j,6);
	}
	for(i=0;i<N;i++){
		query(i,0,7);
		query(i,M-1,5);
	}
	for(j=0;j<M;j++){
		query(N-1,j,7);
		query(N-1,j,5);
	}
	for(i = 0;i<Q;i++){
		int dd = ( ans_dir[i] + 4 ) % 8 ;
		printf("%d %d %c\n",ans_r[i],ans_c[i] ,ddd[dd] );
	}
}
int main(){
	while(scanf("%d%d%d",&N,&M,&Q) == 3){
		for(int i=0;i<N;i++){
			scanf("%s",map[i]);
		}
		root = new Node ; 
		for(int i=0;i<Q;i++){
			scanf("%s",str);
			build_trie(str ,i) ;
		}
		ac_automation() ;
		memset(vis , false ,sizeof(vis) );
		solve() ;
	}	
	return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值