给出一个电话列表,如果列表中存在其中一个号码是另一个号码的前缀这一情况,那么就称这个电话列表是不兼容的。
假设电话列表如下:
- Emergency 911
- Alice 97 625 999
- Bob 91 12 54 26
在此例中,报警电话号码(911)为 Bob 电话号码(91 12 54 26)的前缀,所以该列表不兼容。
输入格式
第一行输入整数 t,表示测试用例数量。
对于每个测试用例,第一行输入整数 n,表示电话号码数量。
接下来 n 行,每行输入一个电话号码,号码内数字之间无空格,电话号码不超过 10 位。
输出格式
对于每个测试用例,如果电话列表兼容,则输出 YES。
否则,输出 NO。
数据范围
1≤t≤40,
1≤n≤10000
输入样例:
2
3
911
97625999
91125426
5
113
12340
123440
12345
98346
输出样例:
NO
YES
算法:
思路一:先用字典树把所有的字符串存储起来,再把所有字符串遍历一遍,在遍历每个字符串的时候,这个字符串的路径中存在其他字符串,那么这个案例是不兼容的;
C++代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n;
int son[N][11],cnt[N],idx;
char str[N][11];
void insert(char *str){
int p = 0;
for(int i = 0; str[i]; i++){
int u = str[i] - '0';
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
cnt[p]++;
}
int query(char *str){
int p = 0, res = 0;
for(int i = 0; str[i]; i++){
int u = str[i] - '0';
if(!son[p][u]) return res;
p = son[p][u];
res += cnt[p];
}
return res;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
memset(son,0,sizeof son);
memset(cnt,0,sizeof cnt);
idx = 0;
scanf("%d",&n);
for(int i = 0; i < n; i++){
scanf("%s",str[i]);
insert(str[i]);
}
bool flag = false;
for(int i = 0; i < n; i++){
if(query(str[i]) > 1) {
flag = true;
break;
}
}
if(flag) puts("NO");
else puts("YES");
}
return 0;
}
思路二:是否存在一个字符串是其他字符串的前缀,只需要判断在存储过程中是否创建的新的节点,和在这一过程中是否已经存在其他字符串;
C++代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n;
int son[N][11],idx;
bool f[N];
char str[N];
bool insert(char *str){
int p = 0;
bool is_new_node = false;
bool is_exist = false;
for(int i = 0; str[i]; i++){
int u = str[i] - '0';
if(!son[p][u]) {
son[p][u] = ++idx;
is_new_node = true;
}
p = son[p][u];
if(f[p]) is_exist = true;
}
f[p] = true;
return is_new_node&&!is_exist;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
memset(son,0,sizeof son);
memset(f,0,sizeof f);
idx = 0;
scanf("%d",&n);
bool flag = false;
for(int i = 0; i < n; i++){
scanf("%s",str);
if(!insert(str)) flag = true;
}
if(flag) puts("NO");
else puts("YES");
}
return 0;
}