tire(前缀树)--Phone List

Description

Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let's say the phone catalogue listed these numbers:

  • Emergency 911
  • Alice 97 625 999
  • Bob 91 12 54 26

In this case, it's not possible to call Bob, because the central would direct your call to the emergency line as soon as you had dialled the first three digits of Bob's phone number. So this list would not be consistent.

Input

The first line of input gives a single integer, 1 ≤ t ≤ 40, the number of test cases. Each test case starts with n, the number of phone numbers, on a separate line, 1 ≤ n ≤ 10000. Then follows n lines with one unique phone number on each line. A phone number is a sequence of at most ten digits.

Output

For each test case, output "YES" if the list is consistent, or "NO" otherwise.

Sample Input

2
3
911
97625999
91125426
5
113
12340
123440
12345
98346

Sample Output

NO
YES

 题意:检查有没有一个字符串是另一个字符串的前缀,如果有输出NO,否则输出YES;

tire 前缀树静态

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
struct tire{
	int count;//记录到此节点的前缀的出现次数 
	struct tire *next_s[10];//记录此节点的下一个节点为字符i的节点 
	bool End;//true表示该节点为一个字符串结点 
	void init(){//成员函数 
		End=false;
		count=0;
		for(int i=0;i<=9;i++){
			//NULL表示还没有插入编号为i的字符,编号的含义看训练指南的208页
			next_s[i]=NULL; 
		}
	}
};
tire root[1<<16];//此题动态申请的方式超时,直接定义静态的 
ll cnt;
//边插入字符边检查 
bool search(tire root[],char *str){
	char *p=str;
	tire *node=&root[0];//从树的根节点开始扫描 
	while(*p){
		//如果下一个字符还没有插入 
		if(node->next_s[(*p)-'0']==NULL){
			//如果该节点已经是某个字符串的结点了
			//那么说明该字符串是目前正在操作的字符串的前缀,直接返回false 
			if(node->End==true)return false;
			//否则继续延伸tire树,cnt用来记录树节点的编号 
			node->next_s[(*p)-'0']=&root[++cnt];
		}
		//移动遍历指针 
		node=node->next_s[(*p)-'0'];
		node->count+=1;//记录一下改前缀出现次数加一 
		++p;
	}
	node->End=true;//让该结束的节点成为字符串结点 
	//如果该结点之前出现过一次,说明该字符串是某个字符串的前缀(包括相等) 
	if(node->count>1)return false; 
	return true;
}
int main(){
	int t;
	scanf("%d",&t);
	getchar();
	while(t--){
		cnt=0;
		int n;
		scanf("%d",&n);
		getchar();
		root[0].init();//根节点初始化 
		char s[20];
		int flag=0;
		for(int i=0;i<n;i++){
			scanf("%s",s);
			getchar();
			//这样的好处一旦找到前缀的重复search函数不会再执行了 
			if(flag==0&&search(root,s)==false){
				flag=1;
			}
		}
		if(flag){
			cout<<"NO"<<endl;
		}
		else cout<<"YES"<<endl;
		//初始化root 
		for(int i=0;i<=cnt;i++){
			root[i].init();
		}
	}
return 0;
}

 tire前缀树动态

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
typedef struct tire{
	int count;
	struct tire* next_s[10];
	bool End;
};
//创建一个节tire点 
tire * creattire(){
	tire *p=new tire;
	p->count=0;
	for(int i=0;i<=9;i++){
		p->next_s[i]=NULL;
	}
	p->End=false;
	return p;
}
bool search(tire * &root,string &str){
	string::const_iterator p=str.begin();
	tire *node=root;
	int cnt=0;
	while(p!=str.end()){
		cnt++;
		if(node->next_s[*p-'0']==NULL){
			if(node->End==true)return false;
			node->next_s[*p-'0']=creattire();//动态申请空间 
		}
		node=node->next_s[*p-'0'];
		node->count+=1;
		++p;
	}
	node->End=true;
	if(node->count>1)return false;
	return true;
}
//销毁tire 
void deletetire(tire *&root){
	tire *node=root;
	tire *t;
	if(node!=NULL){
		for(int i=0;i<=9;i++){
			deletetire(node->next_s[i]);//递归销毁 
		}
		delete node;
	}
}

int main(){
	int t;
	cin>>t;
	while(t--){
		tire* root=creattire();
		int n;
		cin>>n;
		string s;
		int flag=0;
		for(int i=0;i<n;i++){
			cin>>s;
			if(flag==0&&search(root,s)==false){
				flag=1;
			}
		}
		if(flag){
			cout<<"NO"<<endl;
		}
		else cout<<"YES"<<endl;
		deletetire(root);
	}


return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值