poj 1204 trie树/AC自动机(在二维词表中找词)

题意:给定一个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;
}


你可能感兴趣的:(poj 1204 trie树/AC自动机(在二维词表中找词))