1 条题解

  • 0
    @ 2025-8-24 21:22:52

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Terrasse
    **

    搬运于2025-08-24 21:22:51,当前版本为作者最后更新于2018-12-25 13:41:12,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    玄学问题?

    本题的SPJ似乎已经基本没有问题了,只要 文末没有多余的空格和回车 就能正常评测。


    审题

    本题给出了26个字母与数字的对应关系,要求将一串数字翻译为几个单词。

    那么我们是不需要关注各个单词中的字母具体是什么的,只需要存起来输出的时候用一下就行了,翻译过程中完全可以转换为纯数字操作。

    具体地说:the->732she->732,对于密码中的732,它们是完全等价的。所以我们建立一个数组用于转换:

    const char st[26]={1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,8,8,8,9,9,9};
    

    然后题目变成了:给定目标串,要求将它拆解为多个模式串首尾相连的结果。

    AC自动机字典树+深度优先搜索


    Trie

    先上代码:

    inline void init(char *a)
    {
      register int i=0;
      for(;*a;++i,++a)
      {
        b[i]=st[*a-'a'];
      }
      b[i]='\0';
      return;
    }
    
    inline void insert(char *a,const int &id)
    {
      register trie *p=head;
      for(;*a;++a)
      {
        if(p->son[*a])
        {
          p=p->son[*a];
        }
        else
        {
          p=p->son[*a]=++mtp;
        }
      }
      p->end=id;
      return;
    }
    

    我先使用init(str[i])str[i][](第i个单词)转换为纯数字保存在b[]中,然后使用insert(b,i)执行插入操作。为了方便搜到答案之后输出,我将单词的编号i传递进函数,用编号作为Trie中字符串的结尾标记Trie::end,这样就可以方便地一边深搜一边统计答案。

    另外,比如the->732&she->732,就会执行两次完全相同的插入过程,但是我们不需要考虑这个问题,因为按照题意,它们完全等价,end中会保存较晚插入的那个单词的编号,而题目只要求输出一组可行解。


    值得一提的是insert(b,i)前我还进行了特判if(*b)

      for(i=1;i<=n;++i)
      {
        init(str[i]);
        if(*b)insert(b,i);
      }
    

    这个操作的用意是判断b[]是否是空串,理论上来说不可能会有这种情况,可是我切这道题是遇到了Trie的根节点莫名其妙被标记为字符串末尾的现象,就这样解决了......目前还没有查出问题根源,如果大佬们AC了这道题,请劳驾前往这里帮我看看是不是我哪个地方写了。。


    DFS

    先上代码:

    void dfs(int x)
    {
      register trie *p=head;
      for(;a[x];)
      {
        if(p->end)
        {
          ans[++cnt]=p->end;
          dfs(x);
          --cnt;
        }
        if(p->son[a[x]])
        {
          p=p->son[a[x]];
          ++x;
        }
        else
        {
          return;
        }
      }
      if(p->end)
      {
        ans[++cnt]=p->end;
        out();
      }
      return;
    }
    
    变量说明

    x为当前待匹配的数字的下标;

    指针p用于检索Trie树;

    ans[]用于保存当前搜到的状态。

    DFS过程

    我们使用Trie将所有单词记录后,对密码串从前到后扫描,并在Trie中查询,每查到一个单词的末尾标记,就说明这个地方是有可能断开成为一个单词的,就保存这个单词的编号(即每次搜到p->end!=0说明可能要从这里划断,就先ans[++cnt]=p->end,保存这个词的编号,dfs(x)把当前这个待匹配位置留给下一层搜索,如果回溯回来了,cnt--清除即可),从这个地方再递归一层考虑是否可行。如果一直这样到了密码串末尾,就找到了一组解,输出即可;如果到某个地方Trie上匹配不到了,就说明之前某个地方划分错了,应该回溯,return即可。如果一直退回到main()都没找到解,说明No Solution!

    具体地讲(我是指针选手,希望看得惯,话说这道题使用指针影响不大),每次调用dfs()时都先让一个指针p指向head,使用for从参数指定的位置向后扫描。每次循环先看此处是否是一个单词的结尾(if(p->end)),如果是就ans[++cnt]=p->end;dfs(x);进行下一层的匹配,否则查看是否存在后继节点是数字a[x],如果有就p=p->son[a[x]];x++;匹配下一个数字,否则匹配失败GG了,return。就这样一直到密码串末尾(a[x]=='\0'),还没完,需要判断当下这个p->end是不是0,如果是0说明最后面配到的这一小段并不是某个单词的结尾,只能被迫return继续搜索;如果非0,那么我们终于找到了一组答案ans[++cnt]=p->end;保存最后这个单词的编号,out();输出找到的单词序列~~

    代码

    //P1245 电话号码
    #include<cstdio>
    #include<cstdlib>
    #include "memory.h"
    #define MAXN 110
    using namespace std;
    const char st[26]={1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,8,8,8,9,9,9};
    struct trie
    {
      int end;
      trie *son[17];
      inline trie();
    }mmp[MAXN*MAXN],*mtp=mmp,*head=NULL;
    char str[MAXN][MAXN],a[MAXN],b[MAXN];
    int ans[MAXN],cnt=0,n=0;
    inline void read(int&);
    void write(const int&);
    inline void insert(char*,const int&);
    inline void init(char*);
    void dfs(int);
    inline void out();
    int main()
    {
      register int i=0;
      head=++mtp;
      scanf("%d",&n);
      scanf("%s",a);
      for(i=0;a[i];a[i++]^=48);
      for(i=1;i<=n;++i)
      {
        scanf("%s",str[i]);
      }
      for(i=1;i<=n;++i)
      {
        init(str[i]);
        if(*b)insert(b,i);
      }
      dfs(0);
      printf("No Solutions!");
      return 0;
    }
    inline trie::trie()
    {
      end=0;
      memset(son,0,sizeof(son));
      return;
    }
    inline void out()
    {
      register int i=0;
      for(i=1;i<cnt;++i)
      {
        printf("%s ",str[ans[i]]);
      }
      printf("%s",str[ans[cnt]]);
      exit(0);
    }
    void dfs(int x)
    {
      register trie *p=head;
      for(;a[x];)
      {
        if(p->end)
        {
          ans[++cnt]=p->end;
          dfs(x);
          --cnt;
        }
        if(p->son[a[x]])
        {
          p=p->son[a[x]];
          ++x;
        }
        else
        {
          return;
        }
      }
      if(p->end)
      {
        ans[++cnt]=p->end;
        out();
      }
      return;
    }
    inline void init(char *a)
    {
      register int i=0;
      for(;*a;++i,++a)
      {
        b[i]=st[*a-'a'];
      }
      b[i]='\0';
      return;
    }
    inline void insert(char *a,const int &id)
    {
      register trie *p=head;
      for(;*a;++a)
      {
        if(p->son[*a])
        {
          p=p->son[*a];
        }
        else
        {
          p=p->son[*a]=++mtp;
        }
      }
      p->end=id;
      return;
    }
    

    弱弱求给过...

    • 1

    信息

    ID
    245
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者