题意:给定一个n*m大小的二维词表(0<n,m<=1000),然后给出不多于1000个单词。要求给出单词在词表中的位置(首字母位置和旋转方向)
思路1:一开始想也没想就写了暴搜,果断TLE。之后瞄了一眼题解都说用trie或者ac自动机。就算这样还是没想出怎么用trie存词表。原来是用trie存待查单词,对于词表,仍然是暴搜。注意虽然题目中没给每个单词的长度,但是长度上限可以确定,也就是词表的长或宽的上限,即1000。(一开始还在想对角线最长,大概是1000倍的根2,犯傻逼了)
注意不加这句:p->id= 0的话就会wa,加上就ac。一开始我认为题目是special judge,所以加不加没有区别。实际上匹配了一个字符串,后面num就加一,所以如果有串重复匹配,那么到最后可能出现有串没有匹配上的情况,所以匹配完一个串,必须将其置零。
思路2:将待查单词建立DFA,然后在词表中从最长的串去匹配。注意算起始点的方法。
trie树:
#include <stdio.h>
#include <string.h>
#define N 1005
typedef struct node{
int id;
struct node* next[26];
}node;
node *alloc,*root,trie[1414000];
struct output{
int x,y,o;
}res[N];
char s[N][N],t[1444];
int n,m,k;
int orient[8][2] = {{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
void init(){
int i;
alloc = trie;
root = alloc++;
root->id = 0;
for(i = 0;i<26;i++)
root->next[i] = NULL;
}
void insert(char* x,int id){
int i,j;
node *p,*q;
p = root;
for(i = 0;x[i]!='\0';i++){
if(!p->next[x[i]-'A'])
break;
p = p->next[x[i]-'A'];
}
for(;x[i]!='\0';i++){
q = alloc++;
q->id = 0;
for(j = 0;j<26;j++)
q->next[j] = NULL;
p->next[x[i]-'A'] = q;
p = q;
}
p->id = id;
}
int in(int x,int y){
return x>=0&&x<n&&y>=0&&y<m;
}
int search(int x,int y,int ori){
int num=0,xx = x,yy = y;
node *p = root;
while(in(x,y) && (p = p->next[s[x][y]-'A'])){
if(p->id){
num++;
res[p->id].x = xx;
res[p->id].y = yy;
res[p->id].o = ori;
p->id= 0;//不加这句就会wa;一开始以为题目是special judge啊,所以我觉得这句加不加无所谓。实际上匹配了一个字符串,后面num就加一,所以如果有串重复匹配,那么到最后可能出现有串没有匹配上的情况,所以匹配完一个串,必须将其置零
}
x += orient[ori][0];
y += orient[ori][1];
}
return num;
}
void brute(){
int i,j,p,num=0;
for(i = 0;i<n;i++)
for(j = 0;j<m;j++)
for(p = 0;p<8;p++){
num += search(i,j,p);
if(num == k)
return;
}
}
int main(){
int i;
init();
scanf("%d %d %d",&n,&m,&k);
for(i = 0;i<n;i++)
scanf("%s",s[i]);
for(i = 1;i<=k;i++){
scanf("%s",t);
insert(t,i);
}
brute();
for(i = 1;i<=k;i++)
printf("%d %d %c\n",res[i].x,res[i].y,res[i].o+'A');
return 0;
}
思路2:AC自动机
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
int T,n,num,res,m,k;
char s[1005][1005],w[1005];
int out[1005][3],len[1005];
int ori[8][2] = {{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
struct trie{
struct trie *next[26];
struct trie *pre;
bool bad;
int id;
void init(){
memset(next, NULL, sizeof(next));
pre = NULL;
bad = false;
id = -1;
}
}t[1000005],*root;
queue<struct trie*> q;
void insert(char* s,struct trie* r,int id){//建立trie树
for(int i = 0;s[i];i++){
if(r->next[s[i]-'A'] == NULL)
r->next[s[i]-'A'] = t+(++num);
r = r->next[s[i]-'A'];
}
r->bad = true;
r->id = id;
}
void buildDFA(){ //构造DFA
int i;
for(i = 0;i<26;i++) //t[0]是附加结点,方便建立DFA
t[0].next[i] = t+1;
t[1].pre = t;
t[0].pre = NULL;
q.push(t+1);
while (!q.empty()) { //按照层次处理结点,所以需要队列
struct trie *now = q.front(),*p,*pre;
q.pop();
for(i = 0;i<26;i++){
p = now->next[i]; //p是当前要处理的结点
if(p){
pre = now->pre; //前向(失败)结点,目的是找前向结点的next[i]结点
while(pre){
if(pre->next[i]){ //最往上找到t[0],那么t[0].next[i]一定为真,且为根节点
p->pre = pre->next[i];
if(pre->next[i]->bad)
p->bad = true;
break;
}
pre = pre->pre;
}
q.push(p);
}
}
}
}
int check(int x,int y){
return x>=0&&x<n&&y>=0&&y<m;
}
void search(int x,int y,int oo){
int xx,yy;
struct trie *p,*r;
xx = x;
yy = y;
r = t+1;
while(check(xx,yy)){
while(!r->next[s[xx][yy]-'A'])
r = r->pre;
r = r->next[s[xx][yy]-'A']; //往下走一格
p = r;
while(p){
if(p->bad){ //有bad为真说明找到了一个子串
out[p->id][0] = xx - (len[p->id]-1)*ori[oo][0];
out[p->id][1] = yy - (len[p->id]-1)*ori[oo][1];
out[p->id][2] = oo;
p->bad = false;
}
p = p->pre;
}
xx += ori[oo][0];
yy += ori[oo][1];
}
}
int main(){
int i;
scanf("%d %d %d",&n,&m,&k);
num = 1;
for(i = 0;i<n;i++)
scanf("%s",s[i]);
for(i = 0;i<k;i++){
scanf("%s",w);
insert(w,t+1,i);
len[i] = strlen(w);
}
buildDFA();
for(i = 0;i<n;i++){
search(i,0,1);
search(i,0,2);
search(i,0,3);
search(i,m-1,5);
search(i,m-1,6);
search(i,m-1,7);
}
for(i = 0;i<m;i++){
search(0,i,3);
search(0,i,4);
search(0,i,5);
search(n-1,i,7);
search(n-1,i,0);
search(n-1,i,1);
}
for(i = 0;i<k;i++){
printf("%d %d %c\n",out[i][0],out[i][1],out[i][2]+'A');
}
return 0;
}