AC自动机的灵活运用,本题关键是灵活二字。
因为数据不是很大,时间要求也不高的缘故,所以本题有人使用暴力法也过了,有人使用Trie,然后枚举八个方向,也过了,不过速度很慢。
当然有人使用AC自动机没AC的,在讨论区里喊AC自动机超时的,那是因为不会灵活运用,或者是硬套模板的,AC了速度也不会快。
给出本人的算法思路:
1 把需要查找的关键字建立Trie, 然后构造AC自动机
2 查找的时候分八个方向查找,比如棋盘是board[N][M],那么就可以循环i(0->N-1),然后每次把board[i]当做一个文本,做过HDU的key word那道题目的人都知道如何搜索一个文本中有多少关键字出现,这样不就把本题转换为查找文本关键字的问题了。 不过本题是需要八个方向的八个文本都搜索一遍,八是个不大的常数。其他方向如何构造一个文本,就可以自己思考一下,不难。
3 记录结果就看个人的编程能力了,这里的技巧是逆转关键字建立Trie,那么搜索到尾就是每个关键字的开头了。
AC自动机虽然高级,但是也还是一个算法工具,套模板,一大抄的代码没什么意义的,还是彻底消化掉,然后灵活运用才是关键。
对于本题写出高效的算法,那么是超过5星级难度的。
下面给出个上面思路的实现算法,速度很快的。
出处: http://blog.csdn.net/kenden23
#include <stdio.h>
#include <string.h>
#include <queue>
using std::queue;
const int MAX_LEN = 1001;
const int ARR_SIZE = 26;
char board[MAX_LEN][MAX_LEN];
char word[MAX_LEN], gDir[MAX_LEN];
int gIndices[MAX_LEN][2];
int L, C, W;
inline int getIndex(char ch) { return ch-'A'; }
struct Node
{
int n, index;
Node *fail;
Node *arr[ARR_SIZE];
};
void clearNode(Node *p)
{
p->n = 0;
p->index = 0;
p->fail = NULL;
for (int i = 0; i < ARR_SIZE; i++)
{
p->arr[i] = NULL;
}
}
Node gPool[MAX_LEN*MAX_LEN];
int gPoolId;
Node *Trie;
void insertTrie(char w[], int index)
{
Node *pCrawl = Trie;
int len = strlen(w);
for (int i = len-1; i >= 0; i--)//逆向插入字符串,方便找到起头节点
{
int id = getIndex(w[i]);
if (!pCrawl->arr[id])
{
pCrawl->arr[id] = &gPool[gPoolId++];
clearNode(pCrawl->arr[id]);
}
pCrawl = pCrawl->arr[id];
}
pCrawl->n++;
pCrawl->index = index;
}
void buildFail()
{
queue<Node *> qu;
qu.push(Trie);
while (!qu.empty())
{
Node *pCrawl = qu.front();
qu.pop();
for (int i = 0; i < ARR_SIZE; i++)
{
if (!pCrawl->arr[i]) continue;
pCrawl->arr[i]->fail = Trie;
Node *fail = pCrawl->fail;
while (fail)
{
if (fail->arr[i])
{
pCrawl->arr[i]->fail = fail->arr[i];
break;
}
fail = fail->fail;
}
qu.push(pCrawl->arr[i]);
}
}
}
void searchDirect(int r, int c, int rDir, int cDir, char dir)
{
Node *pCrawl = Trie;
while (0<=r && 0<=c && r < L && c < C)
{
int id = getIndex(board[r][c]);
while (!pCrawl->arr[id] && pCrawl != Trie) pCrawl = pCrawl->fail;
if (pCrawl->arr[id])
{
pCrawl = pCrawl->arr[id];
Node *tmp = pCrawl;
while (tmp && tmp->n != -1)
{
if (tmp->n)//record results
{
gIndices[tmp->index][0] = r;
gIndices[tmp->index][1] = c;
gDir[tmp->index] = dir;
}
tmp->n = -1;
tmp = tmp->fail;
}
}
r += rDir, c += cDir; //Watch out, if we use "continue"!
}
}
void searchAll()
{
char dir = 'E', counterDir = 'A';
for (int c = 0; c < C; c++)
{
searchDirect(L-1, c, -1, 0, dir);
searchDirect(0, c, 1, 0, counterDir);
}
dir = 'G', counterDir = 'C';
for (int r = 0; r < L; r++)
{
searchDirect(r, 0, 0, 1, dir);
searchDirect(r, C-1, 0, -1, counterDir);
}
dir = 'H', counterDir = 'D';
for (int c = 0; c < C; c++)
{
searchDirect(0, c, 1, 1, dir);
searchDirect(L-1, c, -1, -1, counterDir);
}
for (int r = 1; r < L; r++)
{
searchDirect(r, 0, 1, 1, dir);
searchDirect(r, C-1, -1, -1, counterDir);
}
dir = 'F', counterDir = 'B';
for (int c = 0; c < C; c++)
{
searchDirect(L-1, c, -1, 1, dir);
searchDirect(0, c, 1, -1, counterDir);
}
for (int r = 0; r < L; r++)
{
searchDirect(r, 0, -1, 1, dir);
searchDirect(r, C-1, 1, -1, counterDir);
}
}
int main()
{
Trie = &gPool[0];
while (scanf("%d %d %d", &L, &C, &W) != EOF)
{
clearNode(Trie);
gPoolId = 1;
getchar();
for (int i = 0; i < L; i++)
{
gets(board[i]);
}
for (int i = 0; i < W; i++)
{
gets(word);
insertTrie(word, i);
}
buildFail();
searchAll();
for (int i = 0; i < W; i++)
{
printf("%d %d %c\n", gIndices[i][0], gIndices[i][1], gDir[i]);
}
}
return 0;
}