很明显,二逼青年的作法不是最优的,当然我们肯定是不想做二逼青年撒~OK,这里来一个高富帅版本的。不过要是想看看屌丝的世界,也可以点回去比较比较区别。前一种方法费时608ms,而这里费时176ms。
下面来说说新的方法,其实就是我们熟知的字典树啦。当然,由于我比较懒,所以在网上截了一个图来说明啦~
把图中的字母换成数字就可以了,当然这个题有它自己的特殊性,每一个节点的next其实都是一个数组。
1、字符串中的数字都是0~9。所以next开到10就够了,而且还可以实现随机访问。
2、由于电话号码一般最长是11位,假设极端的条件下,10^5个数据,每个数据都新占一个节点,最多是10^6+个。所以这里node数组的大小去成10^6+5。这其实不是极端情况下的最大值。
3、node[0]为整个字典树的总的根节点。
由于这里可能需要多次的数据检验,所以没必要不停的动态分配内存,用一个足够大的内存就可以了。
参考代码:
#include<stdio.h>
#include<string.h>
const int N = 1000005;
const int ARR_SIZE = 10;
struct NODE
{
bool IsLast;//标记是不是字符串的最后一个字符
NODE *next[ARR_SIZE];
};
NODE node[N];
const int Size = ARR_SIZE * sizeof(NODE *);
bool Insert(char *str, int &top)
{
int nLen = strlen(str);
NODE *p = &node[0];
for(int i = 0; i < nLen; ++i)
{
//如果其根节点是字符串的尾节点,由题知,这里不需要考虑两个号码完全相同的情况
if(p->IsLast)
return true;
if(p->next[str[i] - '0'] == NULL)//以前不存在,加入
{
node[top].IsLast = (i == nLen - 1) ? 1 : 0;
memset(node[top].next, 0, Size);
p->next[str[i] - '0'] = &node[top];
++top;
}
else if(i == nLen - 1)//当前节点已经存在且是最后的字符了,返回
return true;
p = p->next[str[i] - '0'];
}
return false;
}
int main()
{
int t,n,i;
char str[13];
bool flag;
int top;
scanf("%d", &t);
while(t--)
{
flag = 0;
top = 1;
node[0].IsLast = 0;
memset(node[0].next, 0, Size);
scanf("%d", &n);
for(i = 0; i < n; ++i)
{
scanf("%s", str);
if(!flag)
flag = Insert(str, top);
}
if(flag)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}