[POJ][1204][AC自动机] Word Puzzles

题目大意


给出一个矩阵和一些字符串,求字符串在矩阵中出现的位置及其方向。

思路


AC自动机,遍历四条边,用以每个点为起点向三个方向伸展的字符串作为被匹配串并进行匹配。

代码


#include
#include
using namespace std;

#define MAP_SIZE 1000
#define P_SIZE 1000
#define TOTAL_P 1000

struct trie
//利用结构体来封装字典树的节点。
{
    trie* next[26];
    trie* fail;
    int num;
    int len;
    trie()
    //构造函数。
    {
        for(int i=0;inext[s[i]-'A']==NULL)
            p->next[s[i]-'A']=new trie;
        p=p->next[s[i]-'A'];
    }
    p->num=num;
    p->len=strlen(s);
}

void  build_ac_automation(trie* root)
//利用广搜构建失败指针 
{
    int head=0,tail=0;
    q[tail++]=root;
    while(head!=tail)
    {
        trie* front=q[head++];
        //front为队头元素 
        for(int i=0;inext[i]!=NULL)
            //遍历队头元素的子节点 
            {
                trie* p=front->fail;
                while(p!=NULL)
                //只有根节点的失败指针为NULL 
                {
                    if(p->next[i]!=NULL)
                    //顺着失败指针往回走,直至某个节点,其拥有一个字母为'A'+i的子节点。 
                    {
                        front->next[i]->fail=p->next[i];
                        break;
                    }
                    p=p->fail;
                }
                if(p==NULL)
                //p==NULL说明顺着失败指针往回走的过程中没有找到合适的节点,所以将失败指针指向根节点。 
                    front->next[i]->fail=root;
                q[tail++]=front->next[i];
            }
    }
}

bool range(int i,int j)
//判断坐标(i,j)是否在字谜矩阵范围内。 
{
    return i>=0&&j>=0&&i<L&&jnext[map[i][j]-'A']==NULL&&p!=root)
        //若当前节点的没有一个字符为map[i][j]的儿子且当前节点不是根节点
        //通俗的讲,就是顺着失败指针往回走,直至找到合适的节点或根节点为止。 
            p=p->fail;
        if(p->next[map[i][j]-'A']!=NULL)
        //p->next[map[i][j]-'A']==NULL说明没找到合适的节点,p指针指在根节点上。
            p=p->next[map[i][j]-'A']; 
        trie* temp=p;
        while(temp!=root&&temp->num!=-1)
        //顺着失败指针往回走,一直到根节点。 
        {
            if(temp->len!=0)
            {
                res[temp->num].x=i-dir[d][0]*(temp->len-1);
                res[temp->num].y=j-dir[d][1]*(temp->len-1);
                res[temp->num].dir='A'+d;
            }
            temp->num=-1;
            //标记num为-1,避免重复计算。 
            temp=temp->fail;
        }
    }
}

int main()
{
    trie* root=new trie;
    scanf("%d%d%d",&L,&C,&W);
    getchar();
    for(int i=0;i<L;i++)
        gets(map[i]);
    for(int i=0;i<W;i++)
    {
        gets(P);
        insert(root,P,i);
    }
    build_ac_automation(root);
    for(int i=0;i<L;i++)
    //以字谜矩阵的左右两条边上的点为起点向三个方向伸展的字符串作被匹配串。 
    {
        ac_find(root,i,C-1,5);
        ac_find(root,i,C-1,6);
        ac_find(root,i,C-1,7);
        ac_find(root,i,0,1);
        ac_find(root,i,0,2);
        ac_find(root,i,0,3);
    }
    for(int i=0;i<C;i++)
    //同理,遍历上下两条边。 
    {
        ac_find(root,L-1,i,0);
        ac_find(root,L-1,i,1);
        ac_find(root,L-1,i,7);
        ac_find(root,0,i,3);
        ac_find(root,0,i,4);
        ac_find(root,0,i,5);
    }
    for(int i=0;i<W;i++)
        printf("%d %d %c\n",res[i].x,res[i].y,res[i].dir);
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注