(hdu step 5.2.3)Phone List(Trie实现:在一堆号码中,判断是否有号码是其他号码的前缀)


题目:

Phone List

Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 239 Accepted Submission(s): 96
 
Problem 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:
1. Emergency 911
2. Alice 97 625 999
3. 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
 
 
Source
2008 “Insigma International Cup” Zhejiang Collegiate Programming Contest - Warm Up(3)
 
Recommend
lcy
 

题目分析:

               前一篇博客介绍了这道题的非Trie做法。那么这一篇介绍一下这道题的设计初衷:Trie的基本使用——在前缀匹配中的应用。

以下介绍一下Trie的基本知识点(个人理解,请各位批判地接受)。


1、性质

1)根节点不包含任何字符

2)某一结点的所有子节点包含的字符都不相同。

3)从根节点开始到某一结点所经过的路径就是到该节点为止所能形成的单词。


2、基本操作

1)添加:

假设存在字符串str,Trie树的根结点为root。i=0,p=root。

  1)取str[i],判断p->next[str[i]-97]是否为空,若为空,则建立结点temp,并将p->next[str[i]-97]指向temp,然后p指向temp;若不为空,则p=p->next[str[i]-97];

  2)i++,继续取str[i],循环1)中的操作,直到遇到结束符'\0',此时将当前结点p中的isStr置为true。

void insert(Trie* root,char* s){
	if(root == NULL || s == '\0'){
		return ;
	}

	Trie* p = root;

	while(*s != '\0'){
		if(p->next[*s-'a'] == NULL){
			Trie* temp = malloc(sizeof(Trie));

			int i;
			for(i = 0 ; i < MAX ; ++i){
				temp->next[i] = NULL;
			}
			temp->isStr = false;
			p->next[*s-'a'] = temp;

			p = p->next[*s-'a'];
		}else{
			p = p->next[*s-'a'];
		}

		s++;
	}

	p->isStr = true;
}



2)查询:

假设要查找的字符串为str,Trie树的根结点为root,i=0,p=root

  1)取str[i],判断判断p->next[str[i]-97]是否为空,若为空,则返回false;若不为空,则p=p->next[str[i]-97],继续取字符。

  2)重复1)中的操作直到遇到结束符'\0',若当前结点p不为空并且isStr为true,则返回true,否则返回false。

bool search(Trie* root,char* s){
	Trie* p = root;

	while(p != NULL && *s != '\0'){
		p = p->next[*s-'a'];
		s++;
	}

	return (p != NULL && p->isStr);
}



3)删除:

            以递归的形式进行删除。

void del(Trie* root){
	int i;
	for(i = 0 ; i < MAX ; ++i){
		if(root->next[i] != NULL){
			del(root->next[i]);
		}
	}

	free(root);
}



3、实现的基本思路

1)从根节点开始一次搜索

2)去除关键词的第一个字符,并根据该字符选择相应的子树,然后转到相应的子树中去继续搜索

3)不断地重复2)操作指导完成搜索

4)在某个节点处,如果关键词的所有字符已经被取出。则读出该节点的附加信息。



代码如下:

/*
 * c2.cpp
 *
 *  Created on: 2015年3月7日
 *      Author: Administrator
 */

#include <iostream>
#include <cstdio>

using namespace std;


const int MAX = 10;//每个节点的孩子节点数


bool flag;//用于标记是否存在一个号码是另外一个号码的前缀的情况

typedef struct TrieNode{//字典树Trie结点
	bool isStr;//用于标记到该节点是否形成单词
	struct TrieNode* next[MAX];//每个节点的孩子节点
}Trie;


/**
 * 字典树(前缀树)Trie的插入操作
 * 将字符串s插入根为root的字典树中
 */
void insert(Trie* root,char* s){

	if(root == NULL || s == '\0'){//如果这个字典树为空||需要插入的字符串为空
		return ;//直接返回
	}

	Trie* p = root;

	while(*s != '\0'){//顺序遍历要插入的这个字符串
		if(p->next[*s-'0'] == NULL){//如果目前这个孩子节点还不存在
			//则新建该节点
			Trie* temp = (Trie*)malloc(sizeof(Trie));
			int i;
			for(i = 0 ; i < MAX ; ++i){
				temp->next[i] = NULL;
			}
			temp->isStr = false;
			p->next[*s-'0'] = temp;//将p中的孩子节点指向新建的这个temp结点

			p = p->next[*s-'0'];//更新当前在字典树中查找到的结点
		}else{
			p = p->next[*s-'0'];//更新当前在字典树中查找到的结点
		}


		if(p->isStr == true){//如果当前查找到的结点是之前某一个单词的结束结点,则表明存在某一个号码是另外一个号码的前缀的情况
			flag = true;//将flag标记为true.表明存在某一个号码是另外一个号码的前缀的情况
			return ;//已经能得到结果.返回
		}

		//如果能继续执行以下代码,表明到当前结点为止,不存在号码是另外一个号码的前缀的情况.

		s++;//继续遍历下一个节点
	}

	p->isStr = true;//字符串已经插入完毕.将该节点的isStr标记为true,表明在这里可以形成一个号码

	int i;
	for(i = 0 ; i < MAX ; ++i){//遍历该节点的所有子节点
		if(p->next[i] != NULL){//如果该子节点存在.
			flag = true;//说明该号码是另外一个号码的前缀.将flag标记为true
			break;//跳出循环
		}
	}
}


/**
 * 字典树(前缀树)Trie的查找操作
 * 如果以root为根的字典树中存在s字符串.则返回true,
 * 否则返回false
 */
bool search(Trie* root,char* s){
	Trie* p = root;

	while(p != NULL && *s != '\0'){//如果当前遍历到的结点不为NULL&&字符串s还没有遍历完
		//则继续遍历
		p = p->next[*s-'0'];
		s++;
	}

	return (p != NULL && p->isStr);//判断结束结点是否存在&&是否在该节点出形成一个号码
}


/**
 * 字典树(前缀树)Trie的删除操作
 *
 */
void del(Trie* root){
	int i;
	for(i = 0 ; i < MAX ; ++i){//遍历该节点的所有孩子节点
		if(root->next[i] != NULL){//如果该孩子节点不为NULL
			del(root->next[i]);//递归删除该孩子节点的孩子节点
		}
	}

	free(root);//释放空间
}


int main(){
	int t;
	scanf("%d",&t);
	char s[11];

	while(t--){
		flag = false;

		//初始化操作
		Trie* root = (Trie*)malloc(sizeof(Trie));
		root->isStr = false;
		int i;
		for(i = 0 ; i < MAX ; ++i){
			root->next[i] = NULL;
		}

		int n;
		scanf("%d",&n);
		for(i = 0 ; i < n ; ++i){
			scanf("%s",s);

			if(flag == false){//如果当前还不存在某一个号码是另外一个号码的前缀的情况
				insert(root,s);
			}
		}

		if(flag == true){
			printf("NO\n");
		}else{
			printf("YES\n");
		}

		del(root);//释放空间。如果在这里不释放空间,会导致内存激增而最后MLE
	}

	return 0;
}














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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅气的东哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值