电话列表(前缀树,字典树 典例)

原题链接
给出一个电话列表,如果列表中存在其中一个号码是另一个号码的前缀这一情况,那么就称这个电话列表是不兼容的。

假设电话列表如下:
·Emergency 911
·Alice 97 625 999
·Bob 91 12 54 26
在此例中,报警电话号码(911)为Bob电话号码(91 12 54 26)的前缀,所以该列表不兼容。

输入格式
第一行输入整数t,表示测试用例数量。
对于每个测试用例,第一行输入整数n,表示电话号码数量。
接下来n行,每行输入一个电话号码,号码内数字之间无空格,电话号码不超过10位。

输出格式
对于每个测试用例,如果电话列表兼容,则输出”YES”。
否则,输出”NO”。

数据范围
1≤t≤40
1≤n≤10000

输入样例:
2
3
911
97625999
91125426
5
113
12340
123440
12345
98346

输出样例:
NO
YES

> 分析看到前缀,肯定能想到前缀树(字典树)。

起初是这样想的:字符串的前缀肯定是比自身短的,于是先按长度由小到大排序,之后在插入的时候判断,是否当前的位置是前面某串的结束位置。
如果是,那么前面那个字符串一定是当前字符串的前缀(如果是前面有前缀字符串的话,那当前的字符串一定会循着前面那前缀字符串的路径走,所以前缀字符串的结束位置一定会被当前字符串经过,如此便可判断。然后由于数组大小原因wa了,最后才改过来。。

然后又想了想(查了查… ),发现也可以这样做:存在前缀,无非有两种情况:
1、当前字符串是别的字符串的前缀;2、别的字符串是当前字符串的前缀。
那么,直接在插入的时候判断就可以了:如果始终没有开辟新的路径,那么该字符串是别的字符串的前缀;如果该字符串经过了前面字符串的结束位置,那么前面字符串就是当前字符串的前缀(相当于第一种排过序的思路)。

法1code:

#include<bits/stdc++.h>
using namespace std;
string a[10010];
int tree[100001][11],cnt,t,flag;
int T,n;
bool isEnd[100010];

int cmp(string a,string b){	 
	return a.size()<b.size();
}

int insert(string a){	//插入、判断; 
	int root=0;
	for(int i=0;i<a.size();i++){
		t=a[i]-'0';
		if(!tree[root][t]) tree[root][t]=++cnt;	//另辟路径; 
		root=tree[root][t];
		if(isEnd[root]==true) {flag=1;return 0;} //经过了前面字符串的结束位置; 
	}
	isEnd[root]=true; 	//标记结束位置; 
	return 0;
}

int main(){
	cin>>T;
	while(T--)
	{								//多组数据时,数组和变量的的初始化一定不要忘记; 
		for(int i=0;i<=100010;i++) isEnd[i]=false;
		for(int i=0;i<100001;i++)
			for(int j=0;j<=9;j++)
				tree[i][j]=0;
		cnt=0;
		flag=0;
		
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		sort(a+1,a+1+n,cmp);	//按长度将字符串排序; 
		for(int i=1;i<=n;i++){
			insert(a[i]);	//插入; 
		}
		if(flag==0) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}

法2code:

#include<bits/stdc++.h>
using namespace std;
string a[10010];
int tree[100001][11],cnt,t,flag;
int T,n;
bool isEnd[100010];

int insert(string a){
	int root=0;
	int flag1=0;
	for(int i=0;i<a.size();i++){
		t=a[i]-'0';
		if(!tree[root][t]) flag1=1,tree[root][t]=++cnt;	//如果新辟路经了,记录一下; 
		root=tree[root][t];
		if(isEnd[root]==true) {flag=1;return 0;} //如果经过了前面串的结束位置,说明前面串是该串的前缀,返回; 
	}
	if(flag1==0) {flag=1;return 0;}	//始终没有新辟路经,说明是前面串的前缀,返回; 
	isEnd[root]=true; //标记结束位置; 
	return 0;
}

int main(){
	cin>>T;
	while(T--)
	{									//初始化; 
		for(int i=0;i<=100010;i++) isEnd[i]=false;
		for(int i=0;i<100001;i++)
			for(int j=0;j<=9;j++)
				tree[i][j]=0;
		cnt=0;
		flag=0;
		
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			insert(a[i]);
		}
		if(flag==0) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}

如果有不对或者不明白的地方欢迎留言评论.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值