Luogu P1245 电话号码

谨以此题解致敬我的初中英语老师孙菡老师,她带的班默写不过的人数总是像电话号码那样屈指可数

玄学问题?

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


审题

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

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

具体地说:the->732,she->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;
}

Luogu P1245 电话号码的更多相关文章

  1. [LeetCode] Valid Phone Numbers 验证电话号码

    Given a text file file.txt that contains list of phone numbers (one per line), write a one liner bas ...

  2. [LeetCode] Letter Combinations of a Phone Number 电话号码的字母组合

    Given a digit string, return all possible letter combinations that the number could represent. A map ...

  3. 【代码笔记】iOS-替换电话号码中间4位为-号

    一,效果图. 二,代码. RootViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additional se ...

  4. 国内固定电话正则验证:'tel': [/0\d{2,3}-\d{7,8}(|([-\u8f6c]{1}\d{1,5}))$/, "请填写有效的电话号码"],

    // 验证字段 $('#info_form').validator({ rules : { checkMobile : function(ele) { return checkMobile(ele); ...

  5. C#中使用正则表达式验证电话号码、手机号、身份证号、数字和邮编

      验证电话号码的主要代码如下: public bool IsTelephone(string str_telephone) { return System.Text.RegularExpressio ...

  6. Luogu 魔法学院杯-第二弹(萌新的第一法blog)

    虽然有点久远  还是放一下吧. 传送门:https://www.luogu.org/contest/show?tid=754 第一题  沉迷游戏,伤感情 #include <queue> ...

  7. 防止在iOS设备中的Safari将数字识别为电话号码

    在测试中发现iPad上的Safari总会把长串数字识别为电话号码,文字变成蓝色,点击还会弹出菜单添加到通讯录. 别的地方倒也罢了,如果在用户名中出现数字(手机注册新浪微博的话用户名就是“手机用户xxx ...

  8. java 验证手机号码、电话号码(包括最新的电信、联通和移动号码)

    一.目前的号码段(2016-12-8更新)   二.代码 package com.test; import java.util.regex.Pattern; public class CheckPho ...

  9. php电话号码正则表达式常用例子

    电话号码正则表达式(支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号) 02   03 ((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{ ...

随机推荐

  1. VisualStudioCode创建的asp.net core项目部署到IIS,以及遇到的问题

    一.发布项目 在visual studio code中通过命令“dotnet publish”,如下图: 这里我把发布位置设置到了D:\WebSite\netcoredemo下. 二.设置IIS 0. ...

  2. AWS的EC2实例搭建服务器使用stackoverflow教程

    作为一个技术开发工程师, 一个给力的问题解决方案搜索引擎是十分必要的, stackoverflow作为一个码农必备神器, 存在访问不稳定,有时候打不开的问题,下面介绍如何在亚马逊云服务器上搭建属于自己 ...

  3. python range的用法小题

    题目(1)for i in range(10): print(i) 结果:123456789 题目(2) for lst in range(100): if lst % 7 == 0 and str( ...

  4. jackson 流式API

    http://www.cnblogs.com/lee0oo0/articles/2652528.html Jackson提供了三种可选的JSON处理方法 1.流式API     com.fasterx ...

  5. cocos creator 无法打开项目 dock栏只显示图标问题解决方法

    1.打开项目 2.找到 library和local文件夹 3.清空这两个文件夹的数据 4.问题已解决 原因: 以前生成的旧数据会和新数据发生冲突,把生成的旧数据清理掉就OK了.

  6. 中国省市区json数据 三级联动

    <label> <span>购买地址</span> <select name="PurchaseProvince" style=" ...

  7. 细说@Html.ActionLink()的用法(转)

    细说@Html.ActionLink()的用法   一.@Html.ActionLink()概述 在MVC的Rasor视图引擎中,微软采用一种全新的方式来表示从前的超链接方式,它代替了从前的繁杂的超链 ...

  8. git上clone需要ssh时

    在命令行中输入ssh-keygen -t rsa -C "your_email@example.com" 然后使用编辑器打开id_rsa.pub文件,复制里面的ssh

  9. php----------const 定义的常量和define()定义的常量的区别?

    用法一:const用于类成员变量,一经定义不可修改,define用于全局常量,不可用于类成员变量的定义,const可在类中使用也可以在类外面使用,define不能. 定义:const 常量名=值; 没 ...

  10. NetBeans远程调试Linux c++ 11项目

    NetBeans远程调试Linux c++ 11项目 由于好多原因,好久没有写博客了,随着c++ 11的普及率越来越高,开发c++ 项目的人也越来越多,可以说c++ 11 给了c++ 这门语言第二次生 ...