:输入一串数字,根据数字可以导出一大堆名字,要找出所有既能导出,又存在于字典当中的名字。其中2可以代表A,B,C;3代表D,E,F,依此类推,但是字母Q和Z不存在。
其中字典需要单独读入,文件名为dict.txt
不要被题目骗了
题目叫你通过数字找出所有名字你就通过数字找出所有名字???
那你484撒(对我就是撒
深搜的深度最大为13,需要遍历3^d个搜索树节点。
改变一下策略,反正你要挨个读入字典dict.txt,那还不如反着把字典里头的名字转化成数字代码。
打表:
int map[26]={2,2,2,//ABC
3,3,3,//DEF
4,4,4,//GHI
5,5,5,//JKL
6,6,6,//MNO
7,0,7,7,//PQRS,其中Q不存在所以设为0
8,8,8,9,9,9};
论如何将一个“名字”转化成代码
int transfer(string name){
int ret=0;
for(int i=0;i10;
ret+=map[name[i]-'A'];//数据保证大写
}
return ret;
}
//main()
FILE *d=fopen("dict.txt","r");
char buffer[30];
while(~fscanf(d,"%s",buffer){
if(transfer(buffer)==code){
exist=true;
cout<if(!exist)cout<<"NONE";
我就脑残地建立了两棵字典树+dfs(微笑)虽然时间慢了(每次都要把所有情况算出来)而且空间大了(这不明显么)然而还是过了13组数据(共15组,以wa告终,我错在没有排除Q和Z)我觉得那行吧顺便复习下字典树和dfs,就又写了一遍。(逗逼青年欢乐多)
struct node {
node *child[26];
bool exist;//记录是否为终结点
//终结点是我自己起的名字,我并不知道应该叫什么= =
node() {
for (int i = 0; i < 26; i++)child[i] = 0;
exist = 0;
}
};
class Trie {
node *root;
public:
Trie() {
root = new node;
}
void insert(string str) {
node *scout = root;
for (int i = 0; i < str.length(); i++) {
int target = str[i] - 'A';//题目保证全大写
if (!scout->child[target])scout->child[target] = new node;
scout = scout->child[target];
}
scout->exist = 1;
}
friend void compare(Trie&, Trie&);
};
为了题目,我暂时没有写查询函数,因为不需要(废话)
其中compare(Trie &,Trie &)
是用来同时遍历两棵字典树用的。规则如下:
1、对于任意结点,如果a树中存在但b树中不存在,则不访问此节点
2、对于任意节点,如果此节点同时在a、b两棵树中以终结点(从根结点到此节点能够形成一个存在字典中的单词)则输出此节点的单词。
我的trie树节点的特点是,结点本身不存储自己是’a’还是’b’,链接到上一节点的对应位置,这样既可以保证访问速度又可以保证存储空间。
对任意树遍历方法:
void traversal(node *target, string current) {
for (int i = 0; i < 26; i++)
if(target->child[i])
traversal(target->child[i],current+(char)(i+'a'));
if (target->exist){
//todo:输出节点信息
//optional:计数器++
}
}
/*调用:*/traversal(root,"");
言归正传,现在用这个开始做题
首先实例化两棵字典树all和dict
Trie all,dict;
咱先把那个神奇的数字串dfs一下,把所有可能的名字找出来(逗逼本质暴露的时刻到了)
string key;
void dfsName(int digit, int num, string name) {
if (digit >= key.length()) {
all.insert(name);//向其中一棵字典树中插入能打出的所有名字
return;
}
for (int i = 0; i < 3; i++) {
dfsName(digit + 1, i, name + (char)(i + (key[digit] - '0') * 3 - 6 + 'A'));
//错就错在这了,这里只适用于'A'到'P'之间的名字。
//总之我懒得改了
}
}
//把电话号码读到key里头然后调用:
dfsName(0,0,"");
然后再读取dict.txt…
FILE *dicttxt=fopen("dict.txt","r");
char buffer[30];//13
while(~fscanf(dicttxt,"%s",buffer))dict.insert(buffer);
至此我们有了两棵字典树。接下来我们要做的就是把两颗字典树“重叠”起来,同时遍历,就可以找到所有共有的终结点了。
void compare(node *a, node *b, string current) {
node *sca = a, *scb = b;
for (int i = 0; i < 26; i++) {
if (sca->child[i] && scb->child[i])
compare(sca->child[i], scb->child[i], current + (char)(i + 'A'));
//根据遍历策略,我需要走一步,"顺"一个字母。从上到下连起来成为单词
}
if (sca->exist&&scb->exist){
good=true;
cout << current.c_str() << endl;
}
}
void compare(Trie &a, Trie &b) {
compare(a.root, b.root, "");
//这就是为什么我在trie类中要声明friend void compare (Trie &,Trie &);
}
#include
using namespace std;
struct node {
node *child[26];
bool exist;
node() {
for (int i = 0; i < 26; i++)child[i] = 0;
exist = 0;
}
};
class Trie {
node *root;
public:
Trie() {
root = new node;
}
void insert(string str) {
node *scout = root;
for (int i = 0; i < str.length(); i++) {
int target = str[i] - 'A';
if (!scout->child[target])scout->child[target] = new node;
scout = scout->child[target];
}
scout->exist = 1;
}
friend void compare(Trie&, Trie&);
}all, dict;
bool good=0;
void compare(node *a, node *b, string current) {
node *sca = a, *scb = b;
for (int i = 0; i < 26; i++) {
if (sca->child[i] && scb->child[i])
compare(sca->child[i], scb->child[i], current + (char)(i + 'A'));
}
if (sca->exist&&scb->exist){
good=true;
cout << current.c_str() << endl;
}
}
void compare(Trie &a, Trie &b) {
compare(a.root, b.root, "");
}
string key;
void dfsName(int digit, int num, string name) {
if (digit >= key.length()) {
all.insert(name);
return;
}
for (int i = 0; i < 3; i++) {
char newDigit=(char)(i + (key[digit] - '0') * 3 - 6 + 'A');
if(newDigit>='Q')newDigit++;
dfsName(digit + 1, i, name + newDigit);
}
}
int main() {
freopen("namenum.in","r",stdin);
freopen("namenum.out","w",stdout);
char a[13];
FILE *diccc=fopen("dict.txt","r");
while(~fscanf(diccc,"%s",a))dict.insert(a);
cin >> a;
key = a;
dfsName(0, 0, "");
compare(all, dict);
if(!good)cout<<"NONE";
}
其实,USACO是支持C++11的,也就是说我可以放肆地用STL(standard template library)里的set,不用担心兼容不兼容。所以其产物如下。
#include
using namespace std;
#include
set<string> dict;
bool exist=false;
string key;
void dfsName(int digit, int num, string name) {
if (digit >= key.length()) {
if(dict.find(name)!=dict.end()){
exist=true;
cout<return;
}
for (int i = 0; i < 3; i++) {
char newDigit=(char)(i + (key[digit] - '0') * 3 - 6 + 'A');
if(newDigit>='Q')newDigit++;
dfsName(digit + 1, i, name+newDigit);}
}
int main(){
freopen("namenum.in","r",stdin);
freopen("namenum.out","w",stdout);
FILE *diccc=fopen("dict.txt","r");
while(~fscanf(diccc,"%s",a))dict.insert(a);
cin>>a;key=a;
dfsName(0,0,"");
if(!exist)cout<<"NONE";
}