zoukankan      html  css  js  c++  java
  • PKU 1204 Word Puzzles(AC自动机)

    题目大意;原题链接

    给定一个字符串矩阵和待查找的单词,可以朝8个不同的方向查找,输出待查找单词第一个字母在矩阵中出现的位置和该单词被查到的方向.

    A~H代表8个不同的方向,A代表正北方向,其他依次以45度角的方向顺时针增加.

    解题思路:

    解法一:Trie树暴搜

    因为不查询重复单词,所以dfs(int u,int i,int j,int k)函数中当已经查询到单词时,val[u]可以置零做标记.

    #include<cstdio> 
    #include<cstring>
    #define maxn 200010
    using namespace std;
    bool vis[26];
    int n,m,w,x,y,sz=1;//sz得为全局变量 
    char str[1010][1010],word[1010];
    int val[maxn],out[1010][3];
    int dir[8][2]={-1,0,-1,1,0,1,1,1,1,0,1,-1,0,-1,-1,-1};//顺时针 
    struct Trie
    {
        int next[26];
    }trie[maxn];
    
    void insert(char *s,int k)
    {
        int u=0,len=strlen(s);
        for(int i=0;i<len;i++){
            int id=s[i]-'A';
            if(!trie[u].next[id]) 
                trie[u].next[id]=sz++;
            u=trie[u].next[id];
        }
        val[u]=k;//u为结点编号,k为单词编号 
    }
    void dfs(int u,int i,int j,int k)//k记录方向 
    {
        if(u==0||i<0||i>=n||j<0||j>=m) return;//该语句之前放在if(val[u])条件之后,WA
        if(val[u]){
            out[val[u]][0]=x;
            out[val[u]][1]=y;
            out[val[u]][2]=k;
            val[u]=0;//做标记,因为不查询重复单词 
        }
        int id=str[i+dir[k][0]][j+dir[k][1]]-'A';
        dfs(trie[u].next[id],i+dir[k][0],j+dir[k][1],k);//必须得朝同一方向搜索 
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&w);
        for(int i=0;i<n;i++)
            scanf("%s",str[i]);
        for(int i=1;i<=w;i++){
            scanf("%s",word);
            insert(word,i);
            vis[word[0]-'A']=1;
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(vis[str[i][j]-'A']){ 
                    for(int k=0;k<8;k++){
                        x=i,y=j;//记录下最初的位置,从Trie树根开始向下搜索 
                        dfs(trie[0].next[str[i][j]-'A'],i,j,k);
                    }
                } 
            }
        }
        for(int i=1;i<=w;i++)
            printf("%d %d %c
    ",out[i][0],out[i][1],out[i][2]+'A');
        return 0;
    }

     解法二:AC自动机

    参考链接:哔哩哔哩算法讲堂

    #include<queue>
    #include<cstdio>  
    #include<cstdlib>  
    #include<cstring>   
    #define inf 0x3f3f3f3f
    using namespace std;    
    char str[1010][1010],word[1010];  
    int n,m,w; 
    int dir[8][2]={-1,0,-1,1,0,1,1,1,1,0,1,-1,0,-1,-1,-1};//顺时针  
    char ch[9]="ABCDEFGH";  
    int pos[1010][3];   
    struct TrieNode{  
        int id;//记录第几个字符串   
        TrieNode *next[26],*fail;  
        TrieNode(){  
            id=0,fail=0;  
            memset(next,0,sizeof(next));
        }  
    }*root;  
      
    void InsertNode(char *s,int id)
    {  
        int len=strlen(s)-1;  
        TrieNode *p=root;  
        while(len>=0){//这里将s数组倒着插入字典树,方便匹配时记录匹配的终点即原串的起始点  
            if(!p->next[s[len]-'A'])
                p->next[s[len]-'A']=new TrieNode;  
            p=p->next[s[len--]-'A'];  
        }  
        p->id=id;//id记录单词编号  
    }   
    void Build_AC()
    {
        TrieNode *p,*next;
        queue<TrieNode* > que;
        que.push(root);
        while (!que.empty()){
            p=que.front();
            que.pop();
            for(int i=0;i<26;i++){
                if(p->next[i]){
                    if(p==root) p->next[i]->fail=root;
                    else{
                        TrieNode *temp=p->fail;
                        while(temp){
                             if(temp->next[i]){//temp始终代表next[i]的爸爸,有next[i]这个儿子 
                                 p->next[i]->fail=temp->next[i];
                                break;//寻找最长后缀 
                            }
                            temp=temp->fail;
                        }
                        if(!temp) p->next[i]->fail=root;
                    }
                    que.push(p->next[i]);
                }
            }
        }
    }    
    void Query(int x,int y,int d,int di)
    {  
        TrieNode *p=root,*next;   
        while(x>=0&&y>=0&&x<n&&y<m){  
            while(p&&!p->next[str[x][y]-'A']) p=p->fail;  
            if(!p) p=root;  
            else p=p->next[str[x][y]-'A'];  
            next=p;  
            while(next!=root){  
                if(next->id){//记录原串被匹配的起始点  
                    int k=next->id;  
                    if(pos[k][0]>x||(pos[k][0]==x&&pos[k][1]>y)){  
                        pos[k][0]=x;
                        pos[k][1]=y;
                        pos[k][2]=di;//记录单词整体朝向  
                    }   
                }   
                next=next->fail;   
            }  
            x+=dir[d][0];  
            y+=dir[d][1];  
        }  
    }    
    void Free(TrieNode *p)
    {  
        for(int i=0;i<26;i++){
            if(p->next[i])
                Free(p->next[i]);
        }
        delete p;  
    } 
    
    int main(){  
        scanf("%d%d%d",&n,&m,&w);
        root=new TrieNode;  
        for(int i=0;i<n;i++) 
            scanf("%s",str[i]);  
        for(int i=1;i<=w;i++){  
            scanf("%s",&word);  
            InsertNode(word,i);  
            pos[i][0]=pos[i][1]=inf;  
        }  
        Build_AC();   
        for(int i=0;i<m;i++){
            Query(0,i,3,7),Query(n-1,i,7,3);   
            Query(0,i,4,0),Query(n-1,i,0,4);  
            Query(0,i,5,1),Query(n-1,i,1,5);   
        }//从矩阵上下左右四条边枚举8个方向  
        for(int i=0;i<n;i++){
            Query(i,0,1,5),Query(i,m-1,5,1); 
            Query(i,0,2,6),Query(i,m-1,6,2);  
            Query(i,0,3,7),Query(i,m-1,7,3);  
        } 
        for(int i=1;i<=w;i++) 
            printf("%d %d %c
    ",pos[i][0],pos[i][1],ch[pos[i][2]]);  
        Free(root);   
    }  

    后缀数组倍增解法过一段时间补上

  • 相关阅读:
    rgb三基色与rgba
    HTML标签与属性
    HTML的状态码
    sublime text 编辑器的操作
    驼峰命名法
    css动画速度与三次贝赛尔曲线
    相对路径和绝对路径引发的图片无法显示问题
    Javascript-选择器集合调用方法
    Javascript-商品管理新增/删除/修改功能
    Javascript-随滚轮匀速滑动的浮动广告窗动画
  • 原文地址:https://www.cnblogs.com/freinds/p/6443109.html
Copyright © 2011-2022 走看看