SPOJ WPUZZLES (POJ 1204) 413.Word Puzzles AC自动机

POJ 1204 和SPOJ WPUZZLES一样..换了一下输入输出格式而已..

题目大意:

就是现在有一个游戏,给你一个L*C的字符块,只包含大写英文字母,现在你要在这个块中找到给出的所有的英语单词的位置和摆放方向。

其中要找的单词在块中只出现一次,(实际上多个要找的块不会重合,这个游戏就是这样...)

对于每个要找的字符串,输出其在块中起始位置的坐标和方向,方向只有8种,并且单词摆放不会拐弯..就是一条线摆放


大致思路:

其实就是个简单的模板题...首先将所有要找的字符串建立AC自动机,标号其结尾点,然后枚举矩阵的所有串,当然起点和重点都是边界,实际上要枚举的串并不多,找到串的时候利用串的长度和方向就可以很快算出起点位置,最后输出就可以了...


代码如下:

Result  :  Accepted     Memory  :  113664 KB     Time  :  320 ms

/*
 * Author: Gatevin
 * Created Time:  2014/11/26 15:30:39
 * File Name: Kagome.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

int t;
int R, C, W;
char m[1001][1001];
char s[1001];

int dx[] = {-1, -1, 0, 1, 1, 1, 0, -1}; 
int dy[] = {0, 1, 1, 1, 0, -1, -1, -1};

struct Ans
{
    int x, y, dir;
};

Ans ans[1001];
int len[1001];
int next[1000001][26], fail[1000001], end[1000001];

struct Trie
{
    int L, root;
    int newnode()
    {
        for(int i = 0; i < 26; i++)
            next[L][i] = -1;
        end[L++] = -1;//end[i] = -1表示点i不是字符串结尾点
        return L - 1;
    }
    void init()
    {
        L = 0;
        root = newnode();
        return;
    }
    void insert(char* in, int id)
    {
        int now = root;
        for(; *in; in++)
        {
            if(next[now][*in - 'A'] == -1)
                next[now][*in - 'A'] = newnode();
            now = next[now][*in - 'A'];
        }
        end[now] = id;
        return;
    }
    void build()
    {
        fail[root] = root;
        queue <int> Q;
        Q.push(root);
        while(!Q.empty())
        {
            int now = Q.front();
            Q.pop();
            for(int i = 0; i < 26; i++)
                if(next[now][i] == -1)
                    next[now][i] = now == root ? root : next[fail[now]][i];
                else
                {
                    fail[next[now][i]] = now == root ? root : next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
        return;
    }
    void find(int x, int y, int dir)
    {
        int now = root;
        while(x >= 0 && y >= 0 && x < R && y < C)
        {
            now = next[now][m[x][y] - 'A'];
            /*
             * 由于是字符块游戏,不会出现两块有重叠部分出现的情况
             * 所以这里可以不用while(tmp != root) tmp = fail[tmp];这一段
             */
            if(end[now] >= 0)//找到字符串了
            {
                ans[end[now]].x = x - (len[end[now]] - 1)*dx[dir];//计算起点位置
                ans[end[now]].y = y - (len[end[now]] - 1)*dy[dir];
                ans[end[now]].dir = dir;
            }
            x += dx[dir];
            y += dy[dir];
        }
        return;
    }
};

Trie AC;

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        AC.init();
        scanf("%d %d %d", &R, &C, &W);
        for(int i = 0; i < R; i++)
            scanf("%s", m[i]);
        for(int i = 0; i < W; i++)
        {
            scanf("%s", s);
            AC.insert(s, i);
            len[i] = strlen(s);
        }
        AC.build();
        /*
         * 将模板串建立AC自动机之后枚举矩形中所有的串就可以了
         */
        for(int i = 0; i < R; i++)
        {
            AC.find(i, 0, 1); AC.find(i, 0, 2); AC.find(i, 0, 3);
            //左边起点,方向为右上,右,右下的字符串
            AC.find(i, C - 1, 5); AC.find(i, C - 1, 6); AC.find(i, C - 1, 7);
            //右边起点,方向为左上,左,左下的字符串
        }
        for(int i = 0; i < C; i++)
        {
            AC.find(0, i, 3); AC.find(0, i, 4); AC.find(0, i, 5);
            //上边为起点,方向右下,下,左下
            AC.find(R - 1, i, 7); AC.find(R - 1, i, 0); AC.find(R - 1, i, 1);
            //下边为起点,方向左上,上,右上
        }
        for(int i = 0; i < W; i++)
            printf("%d %d %c\n", ans[i].x, ans[i].y, ans[i].dir + 'A');
         if(t) printf("\n");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值