hdu 1671 phone list(Trie 树)
Phone List
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3544 Accepted Submission(s): 1191
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.
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
题目意思很清楚:就是判断输入的电话号码中是否有号码是其他号码的前缀,很显然要用到字典树。根据分析可知:
如果字符串Xn=X1X2....Xn是字符串Ym=Y1Y2....Ym的前缀,有在插入的时候有两种情况:Xn在Yn之前插入,Xn在Yn之后插入。
1)如果Xn在Yn之前插入,那么在插入Yn的时候必然经过Xn的路径,此时可以根据判断在这条路径上是否已经有结点被标记已经构成完成的字符串序列来判断是否存在Yn的前缀;
2)如果Xn在Yn之后插入,那么插入完成之后当前指针指向的结点的next数组中的元素必定不全为NULL。
实现代码:
/*hdu 1671 phone list Trie树的应用 2011.10.15*/
#include <iostream>
#define MAX 10
using namespace std;
bool flag;
typedef struct Trie_Node
{
bool isphone;
struct Trie_Node *next[MAX];
}Trie;
void insert(Trie *root,char *phone)
{
Trie *p=root;
while(*phone!='\0')
{
if(p->next[*phone-48]==NULL)
{
Trie *temp=(Trie *)malloc(sizeof(Trie));
temp->isphone=false;
for(int i=0;i<MAX;i++)
{
temp->next[i]=NULL;
}
p->next[*phone-48]=temp;
}
if(p->isphone==true)
flag=true;
p=p->next[*phone-48];
phone++;
}
p->isphone=true;
for(int i=0;i<MAX;i++)
{
if(p->next[i]!=NULL)
{
flag=true;
break;
}
}
}
void del(Trie *root)
{
for(int i=0;i<MAX;i++)
{
if(root->next[i]!=NULL)
{
del(root->next[i]);
}
}
free(root);
}
int main(int argc, char *argv[])
{
int t;
scanf("%d",&t);
while(t--)
{
int i;
int n;
char phone[11];
Trie *root=(Trie *)malloc(sizeof(Trie));
root->isphone=false;
flag=false;
for(i=0;i<MAX;i++)
{
root->next[i]=NULL;
}
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%s",phone);
if(flag!=true)
{
insert(root,phone);
}
}
if(flag==true)
printf("NO\n");
else
printf("YES\n");
del(root); //必须释放空间,否则会报Memory Limited的错误
}
return 0;
}
当然这道题目也有另一种思路,就是先对电话号码序列进行排序,然后判断相邻的两个号码是否符合条件即可。这种思路利用了一种性质:
存在若干个字符串序列,对于两个字符串序列X[1....n],Y[1....m],如果X是Y的前缀,则在对所有的字符串序列按字典顺序排序后,
X[1..n]和Y[1....m]在位置上必然是直接相邻或者间接相邻的。间接相邻指存在字符串序列Z[1....t],使得X是Z的前缀(X和Z直接相邻),Z是Y的前缀(Z和Y直接相邻),则X和Y也被认为是相邻的。下面证明这条性质:
假设X[1...n]是Y[1....m]的前缀,但是X和Y不相邻,即至少在X和Y之间存在一字符串序列Z[1....t],其中X不是Z的前缀,Z也不是Y的前缀,且字典顺序为X<Z<Y。
证明:
X不是Z的前缀,并且Z的字典顺序比X大,那么只存在两种情况:
1)n>=t,这种情况下,必定存在一个i(0<=i<=t)使得X[i]<Z[i],而由假设可知X是Y的前缀,则必有X[i]=Y[i],此时Y的字典顺序小于Z,与条件不符,不成立。
2)n<t,这种情况下,有两种可能,一种是对于所有的i(0<=i<=n),X[i]=Z[i],但是如此一来X是Z的前缀,与条件不符,不成立;
另一种可能是存在i(0<=i<=n),使得X[i]<Z[i],与1)中所述情况一样,与条件不符,不成立。
因此假设不成立,从而性质得证。
因此可以利用此性质解决这道题目,题目只需判断是否存在一个号码是否是另一个号码的前缀即可。所以可以先对所有的号码序列进行字典排序,然后判断直接相邻的两个号码是否符号条件即可。如果所有直接相邻的号码都不存在一个号码是另一个号码的前缀,那么这些号码序列中肯定不存在一个号码是另一个号码的前缀。
实现代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
char phone[10000][11];
int cmp(const void* a,const void* b)
{
return strcmp((char*)a,(char*)b);
}
int main(void)
{
int t;
scanf("%d",&t);
while(t--)
{
int i,n;
bool flag=false;
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%s",phone[i]);
}
qsort(phone,n,sizeof(phone[0]),cmp);
for(i=0;i<n-1;i++)
{
if(strncmp(phone[i],phone[i+1],strlen(phone[i]))==0)
{
flag=true;
break;
}
}
if(flag==false)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}