nkoj 1931
Description
何老板的手机很先进,当要拨打一个号码时,你需敲出该号码的前面几个数字,手机就会自动找出以该数字为前缀的所有号码。
比如下列电话薄:
TOM:1388
JIM:13885599999
LEE:13812345678
TEC:0236588888
如果何老板敲了138三个数字,手机屏幕上就会显示TOM、JIM和LEE的名字。很是方便呀!
但是何老板发现有不好的地方就是比如他想拨打JIM的号码,当他在手机上敲了1388这四个数字,没等何老板敲完其它数字,手机就会自动拨打TOM的号码,这让何老板很是烦恼。也就是说一旦手机发现你当前敲出的数字与电话薄中的某个号码匹配了,它就会自动拨打该号码。
于是何老板想知道,他的电话薄中,也就是是否存在一个号码是其它号码的前缀的情况。
Input
第一行,一个整数t,表示有t组测试数据1 ≤ t ≤ 40
对于每组测试数据:
第一行一个整数n(1 ≤ n ≤ 10000),表示电话薄中有n个号码
接下来n行,每行一个数字,表示一个电话号码,每个电话号码的长度不超过10,并且是唯一的。
Output
每组测试数据输出一行,如果有有号码是其它号码的前缀,输出"NO"否则输出"YES"
Sample Input
2
4
1388
13885599999
13812345678
0236588888
5
113
12340
123440
12345
98346
Sample Output
NO
YES
分析:
使用 Trie,在插入一个新单词前判断是否是前缀即可:
如果没有添加节点完成了插入操作,或者插入过程中走到了别的单词的结尾,就说明有前缀出现。
注意有多组数据,每次要初始化。
可以使用 int 代替string,但是string的应用范围更广。
当然,使用 Trie一定要考虑数据范围,否则很容易RTE。
代码如下:
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int maxn=100005,maxnode=200005; int n; bool ok; struct node { int num,next[10]; }; struct Trie{ int size; string s[maxn]; node trie[maxn]; void set (){ size=1; memset(trie,0,sizeof(trie)); } int idx(char x){ return (int) x-'0';} bool insert(string a,int id){ int i,p=1,t,len=a.length(); bool sub=true; //是否新建了节点 for(i=0;i<len;i++) if(isdigit(a[i])){ if(trie[p].num) return true; //走到了别的单词结尾 t=idx(a[i]); if(!trie[p].next[t]){ sub=false; p = trie[p].next[t]= ++size; trie[p].num=0; } else p=trie[p].next[t]; } trie[p].num=id; return sub; } } solver; int main(){ ios_base::sync_with_stdio(false); int t,i; string num; cin>>t; while(t--){ solver.set(); //初始化 cin>>n; ok=false; for(i=1;i<=n;i++){ cin>>num; if(!ok&&solver.insert(num,i)){ //出现了前缀 cout<<"NO"<<endl; ok=true; } } if(!ok)cout<<"YES"<<endl; } }//本题中没有使用的Trie 常用函数 find()一并附上:
int find(string a){ int i,p=1,len=a.length(),t; for(i=0;i<len;i++) if(isalpha(a[i])){ t=idx(a[i]); if(!trie[p].next[t]) return 0; p=trie[p].next[t]; } return trie[p].num; }