题目大意

给出一个RxC的字符组成的puzzle,中间可以从左向右,从右到左,从上到下,从下到上,从左上到右下,从右下到左上,从左下到右上,从右上到左下,八个方向进行查找字符串。 
    给出M个字符串,找出他们在puzzle中的位置,返回该字符串在puzzle中的起点横纵坐标以及方向。

字符串长度L <=1000, R,C <= 1000, W <= 1000

题目分析

多模式串的字符串匹配问题,考虑使用Trie图。将M个待查的字符串作为模式串插入Trie图中,然后设置前缀指针,构造DFA。 
    查找的时候,在puzzle的四个边上每个点,沿8个方向分别确定最长的串作为母串,用母串在Trie图上进行行走,进行匹配。

题目比较坑的是,special judge没能对有些正确的结果给AC,只能按照“正常”的顺序来查找。

实现(c++)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<vector>
#include<deque>
using namespace std;
#define MAX_SIZE 1005
#define LETTERS 26
#define MAX_NODES 150000
#define MIN(a, b) a < b? a :b
char gPizza[MAX_SIZE][MAX_SIZE];
struct PosInfo{
int row;
int col;
int dir;
void SetInfo(int r, int c, int d){
row = r;
col = c;
dir = d;
}
};
PosInfo gPosInfo[MAX_SIZE]; struct Node{
Node* childs[LETTERS];
bool danger_node;
Node* prev;
int pattern_index;
Node(){
memset(childs, 0, sizeof(childs));
prev = NULL;
danger_node = false;
pattern_index = 0; //可以指示某个终止节点确定的字符串是第几个pattern
}
};
Node gNodes[MAX_NODES];
int gNodeCount = 2; void Insert(Node* root, char* str, int pattern_index){
Node* node = root;
char*p = str;
while (*p != '\0'){
int index = *p - 'A';
if (! node->childs[index]){
node->childs[index] = gNodes + gNodeCount++;
}
node = node->childs[index];
p++;
}
node->danger_node = true;
node->pattern_index = pattern_index;
} void BuildDfa(){
Node* root = gNodes + 1;
for (int i = 0; i < LETTERS; i++){
gNodes[0].childs[i] = root;
}
root->prev = gNodes;
gNodes[0].prev = NULL; deque<Node*> Q;
Q.push_back(root);
while (!Q.empty()){
Node* node = Q.front();
Node* prev = node->prev, *p;
Q.pop_front();
for (int i = 0; i < LETTERS; i++){
if (node->childs[i]){
p = prev;
while (p && !p->childs[i]){
p = p->prev;
}
node->childs[i]->prev = p->childs[i];
//这个地方注意,不能写成 p->childs[i]->danger_node = node->childs[i]->danger_node
if (p->childs[i]->danger_node)
node->childs[i]->danger_node = true;
Q.push_back(node->childs[i]);
}
}
}
} bool gPatterFind[MAX_SIZE];
int gPatternFoundNum = 0;
int gMinPatternLen = 0;
int gPatternLen[MAX_SIZE];
int gMoveStep[8][2] = { { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, {0, -1 }, { -1, -1 } }; //在Trie图上达到一个“危险”节点,则该节点的各个前缀指针,仍然可能为“终止”节点,沿前缀指针找出所有的终止节点,以防止遗漏
//比如 ABCDFF 中查找 ABCD CD 若到达D,确定为一个危险节点,可以找到ABCD,若不沿着前缀指针找出所有的终止节点,则会遗漏CD
void FindPatternFromEndPoint(Node* node, int r, int c, int dir){
do{
if (node->pattern_index == 0){
node = node->prev;
continue;
} int pattern_index = node->pattern_index;
if (gPatterFind[pattern_index]){ //此时找到的串,有可能是别的串的前缀,因此继续向后找
node = node->prev;
continue;
}
gPatterFind[pattern_index] = true;
gPatternFoundNum++; int beg_r = r - gPatternLen[pattern_index] * gMoveStep[dir][0];
int beg_c = c - gPatternLen[pattern_index] * gMoveStep[dir][1];
if (gMoveStep[dir][0] == 0)
beg_r--; if (gMoveStep[dir][1] == 0)
beg_c--; if (dir == 1 || dir == 7 || dir == 0){
beg_r -= 2;
}
if (dir == 5 || dir == 7 || dir == 6){
beg_c -= 2;
}
gPosInfo[pattern_index].SetInfo(beg_r, beg_c, dir); node = node->prev;
} while (node); }
//从某个边界点出发,沿某个方向的最长字符串作为母串,在Trie图上进行查找
void SearchStr(int start_x, int start_y, int dir){
int r = start_x, c = start_y;
Node* node = gNodes + 1;
while (gPizza[r][c] != '\0'){
int index = gPizza[r][c] - 'A';
while (node && node->childs[index] == NULL){
node = node->prev;
}
node = node->childs[index];
if (node->danger_node){
FindPatternFromEndPoint(node, r, c, dir);
}
r += gMoveStep[dir][0];
c += gMoveStep[dir][1];
}
}
//确定在边界上的某个点,沿某个方向所构成最长字符串的长度
int MaxLen(int R, int C, int r, int c, int dir){
if (dir == 0 || dir == 4)
return R;
if (dir == 2 || dir == 6)
return C;
if (dir == 1){
if (c == 1)
return r;
else if (r == R)
return C - c + 1;
}
if (dir == 5){
if (r == 1)
return c;
else if (c == C)
return R - r + 1;
}
if (dir == 3){
if (r == 1)
return C - c + 1;
if (c == 1)
return R - r + 1;
}
if (dir == 7){
if (r == R)
return c;
if (c == C)
return r;
}
return -1;
} //对边界上的每个点,在8个方向进行查找
void SearchPuzzle(int R, int C, int total_word_to_find){
for (int r = 1; r <= R; r++){
for (int dir = 0; dir < 8; dir++){
if (gPatternFoundNum == total_word_to_find){
return;
}
if (MaxLen(R, C, r, 1, dir) >= gMinPatternLen){
SearchStr(r, 1, dir);
}
}
}
for (int r = 1; r <= R; r++){
for (int dir = 0; dir < 8; dir++){
if (gPatternFoundNum == total_word_to_find){
return;
}
if (MaxLen(R, C, r, C, dir) >= gMinPatternLen){
SearchStr(r, C, dir);
}
}
}
for (int c = 1; c <= C; c++){
for (int dir = 0; dir < 8; dir++){
if (gPatternFoundNum == total_word_to_find){
return;
}
if (MaxLen(R, C, 1, c, dir) >= gMinPatternLen){
SearchStr(1, c, dir);
}
}
}
for (int c = 1; c <= C; c++){
for (int dir = 0; dir < 8; dir++){
if (gPatternFoundNum == total_word_to_find){
return;
}
if (MaxLen(R, C, R, c, dir) >= gMinPatternLen){
SearchStr(R, c, dir);
}
}
}
} int main(){
int R, C, M;
scanf("%d %d %d", &R, &C, &M);
memset(gPizza, 0, sizeof(gPizza));
memset(gPatterFind, false, sizeof(gPatterFind));
gNodeCount = 2; for (int r = 1; r <= R; r++){
getchar();
for (int c = 1; c <= C; c++){
scanf("%c", &gPizza[r][c]);
}
}
getchar();
char str[MAX_SIZE];
Node* root = gNodes + 1;
for (int i = 1; i <= M; i++){
scanf("%s", str);
Insert(root, str, i);
gPatternLen[i] = strlen(str);
gMinPatternLen = MIN(gMinPatternLen, gPatternLen[i]);
} BuildDfa(); SearchPuzzle(R, C, M);
for (int i = 1; i <= M; i++){
printf("%d %d %c\n", gPosInfo[i].row, gPosInfo[i].col, gPosInfo[i].dir + 'A');
}
return 0;
}

poj_1204 Trie图的更多相关文章

  1. 【BZOJ-2938】病毒 Trie图 + 拓扑排序

    2938: [Poi2000]病毒 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 609  Solved: 318[Submit][Status][Di ...

  2. 【hihoCoder】1036 Trie图

    题目:http://hihocoder.com/problemset/problem/1036 给一个词典dict,词典中包含了一些单词words.要求判断给定的一个文本串text中是否包含这个字典中 ...

  3. 【hihoCoder 1036】Trie图

    看了一下简单的$Trie图$,调模板调啊调一连调了$2h$,最后发现$-'a'$打成$-'A'$了hhh,有种摔键盘的冲动. $Trie图$是$Trie树$上建立“前缀边”,不用再像在$Trie树$上 ...

  4. 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组

    涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...

  5. Trie图和Fail树

    Trie图和AC自动机的区别 Trie图是AC自动机的确定化形式,即把每个结点不存在字符的next指针都补全了.这样做的好处是使得构造fail指针时不需要next指针为空而需要不断回溯. 比如构造ne ...

  6. hdu2457 Trie图+dp

    hdu2457 给定n个模式串, 和一个文本串 问如果修改最少的字符串使得文本串不包含模式串, 输出最少的次数,如果不能修改成功,则输出-1 dp[i][j] 表示长度为i的字符串, 到达状态j(Tr ...

  7. Trie图

    AC自动机是KMP的多串形式,当文本串失配时,AC自动机的fail指针告诉我们应该跳到哪里去继续匹配(跳到当前匹配串的最长后缀去),所以AC自动机的状态是有限的 但是AC自动机具有不确定性, 比如要求 ...

  8. CF 291E. Tree-String Problem [dfs kmp trie图优化]

    CF291E 题意:一棵树,每条边上有一些字符,求目标串出现了多少次 直接求目标串的fail然后一边dfs一边跑kmp 然后就被特殊数据卡到\(O(n^2)\)了... 因为这样kmp复杂度分析的基础 ...

  9. AC自动机相关Fail树和Trie图相关基础知识

    装载自55242字符串AC自动机专栏 fail树 定义 把所有fail指针逆向,这样就得到了一棵树 (因为每个节点的出度都为1,所以逆向后每个节点入度为1,所以得到的是一棵树) 还账- 有了这个东西, ...

随机推荐

  1. ubuntu下 apache phpmyadmin 的安装和使用

    // Apache //Task: Start Apache 2 Server /启动apache服务 # /etc/init.d/apache2 start //or $ sudo /etc/ini ...

  2. mysql客户端(Navicat)远程登录操作遇到问题1142 - XXX command denied to ×××

    新建的dbtest用户,访问新建立的TestDB数据库每次都提示1142错误(已经对数据库TestDB赋予了所有权限) 解决方法: Mysql命令: GRANT SELECT ON mysql.* T ...

  3. Bars, rectangles with bases on x-axis

    Usage geom_bar(mapping = NULL, data = NULL, stat = "bin", position = "stack", .. ...

  4. Python:基本运算、基本函数(包括复数)、Math模块、NumPy模块

    基本运算 x**2 : x^2 若x是mat矩阵,那就表示x内每个元素求平方 inf:表示正无穷 逻辑运算符:and,or,not 字典的get方法 a.get(k,d) 1 1 get相当于一条if ...

  5. AJAX乱码解决新方法

    用过AJAX的朋友肯定知道javascript是使用UTF-8国际编码,即每个汉字用3个字节来存储,但是这就造成了用AJAX来send数据的时候出现乱码.     有一种解决办法就是使用encodeU ...

  6. 25个非常实用的jQuery/CSS3应用组件

    今天分享25款功能十分强大的jQuery/CSS3应用插件,欢迎收藏. 1.jQuery水晶样式下拉导航 这是一款非常不错的jQuery多功能下拉菜单插件,菜单外观呈水晶样式,晶莹剔透,功能丰富,包含 ...

  7. (转)YUV420存储格式

    YUV格式有两大类:planar和packed.对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V.对于packed的YUV格式,每个像素点的Y,U ...

  8. 谷哥的小弟学后台(04)——MySQL(4)

    探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 具体解释Android主流框架不可或缺的基石 站在源代码的肩膀上全解Scroller工作机制 Android多分辨率适 ...

  9. Unity中坐标系转换方法

    前言 本篇文章主要是参考<Unity API 解析>---陈泉宏. 这是本人在学校图书馆找到一本书,主要介绍的就是常用的类,比较实用,没有冗余的地方.在此推荐一下这本书! 一.Screen ...

  10. 终于AC了“最短路径”

    今天做了一道关于最短路径的算法题,虽然最后AC了,但是我写的第一个算法,我认为是没有问题的,自己编写的测试用例也全部通过,反复调试了,都没有错误.可是在OJ上一提交就提示Wrong Answer,真是 ...