Trie——电话簿

nkoj 1931

Description

何老板的手机很先进,当要拨打一个号码时,你需敲出该号码的前面几个数字,手机就会自动找出以该数字为前缀的所有号码。 
比如下列电话薄: 
TOM:1388 
JIM:13885599999 
LEE:13812345678 
TEC:0236588888 
如果何老板敲了138三个数字,手机屏幕上就会显示TOM、JIM和LEE的名字。很是方便呀! 

但是何老板发现有不好的地方就是比如他想拨打JIM的号码,当他在手机上敲了1388这四个数字,没等何老板敲完其它数字,手机就会自动拨打TOM的号码,这让何老板很是烦恼。也就是说一旦手机发现你当前敲出的数字与电话薄中的某个号码匹配了,它就会自动拨打该号码。 

于是何老板想知道,他的电话薄中,也就是是否存在一个号码是其它号码的前缀的情况。 

Input

第一行,一个整数t,表示有t组测试数据1 ≤ t ≤ 40 
对于每组测试数据: 
第一行一个整数n(1 ≤ n ≤ 10000),表示电话薄中有n个号码 
接下来n行,每行一个数字,表示一个电话号码,每个电话号码的长度不超过10,并且是唯一的。

Output

每组测试数据输出一行,如果有有号码是其它号码的前缀,输出"NO"否则输出"YES"

Sample Input

2
4
1388
13885599999
13812345678
0236588888
5
113
12340
123440
12345
98346

Sample Output

NO
YES

分析:
使用 Trie,在插入一个新单词前判断是否是前缀即可:
如果没有添加节点完成了插入操作,或者插入过程中走到了别的单词的结尾,就说明有前缀出现。
注意有多组数据,每次要初始化。
可以使用 int 代替string,但是string的应用范围更广。
当然,使用 Trie一定要考虑数据范围,否则很容易RTE。
代码如下:

#include<iostream>  
#include<cstring> 
#include<cstdio>  
using namespace std; 
const int maxn=100005,maxnode=200005;
int n;
bool ok;
struct node {
	int num,next[10];
};
struct Trie{
	int size;
	string s[maxn];
	node trie[maxn];
	void set (){
		size=1;
		memset(trie,0,sizeof(trie));
	}
	int idx(char x){ return (int) x-'0';} 
	bool insert(string a,int id){
		int i,p=1,t,len=a.length();
		bool sub=true;  //是否新建了节点 
		for(i=0;i<len;i++)
			if(isdigit(a[i])){
				if(trie[p].num) return true;  //走到了别的单词结尾 
				t=idx(a[i]);
				if(!trie[p].next[t]){
					sub=false;  
					p = trie[p].next[t]= ++size;
					trie[p].num=0;
				} 
				else p=trie[p].next[t];
			}
		trie[p].num=id;
		return sub;
	}
} solver;
int main(){
	ios_base::sync_with_stdio(false);
	int t,i;
	string num;
	cin>>t;
	while(t--){
		solver.set();  //初始化 
		cin>>n; 
		ok=false;
		for(i=1;i<=n;i++){
			cin>>num;
			if(!ok&&solver.insert(num,i)){  //出现了前缀 
				cout<<"NO"<<endl;
				ok=true;
			}
		}
		if(!ok)cout<<"YES"<<endl;
	}
}

//本题中没有使用的Trie 常用函数 find()一并附上:

	int find(string a){
		int i,p=1,len=a.length(),t;
		for(i=0;i<len;i++) if(isalpha(a[i])){
			t=idx(a[i]);
			if(!trie[p].next[t]) return 0;
			p=trie[p].next[t]; 
		}
		return trie[p].num;
	}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值