题目来源:计蒜客https://www.jisuanke.com/minicourse/812/42071
蒜头君作为蒜厂的工程师,在开发网站时不小心写出了一个 Bug:当用户输入密码时,如果既和自己的密码一致,也同时是另一个用户密码的 前缀 时,用户会跳转到 404 页。
然而蒜头君坚称:我们的用户那么少,怎么可能触发这个 Bug……
机智的你,能不能帮蒜头君确认一下这个 Bug 到底会不会触发呢?
样例输入
第一行输入一个整数n(1≤n≤233333),表示蒜厂网站的用户数。接下来一共 n 行,每行一个由小写字母a-z
组成的字符串,长度不超过 10,表示每个用户的密码。蒜厂的数据库容量太小,所有密码长度加起来小于 466666。
样例输出
如果触发了 Bug 则输出一行Bug!
,否则输出一行Good Luck!
。
样例输入1
3
abc
abcdef
cdef
样例输出1
Bug!
样例输入2
3
abc
bcd
cde
样例输出2
Good Luck!
题目类型就是串匹配,找前缀,多串情况下,很应该想到前缀树,也称为Trie树。
这里首先需要把所有串都读下来插入之后再进行匹配,因为你不能确定一定是前面的是后面的前缀。
对cnt处理的时候 也不能像这个Trie树处理一样,即:
需要注意的是,本题这里需要找一个串是否为另一个串的前缀,与前缀树的一个小小区别是,前缀树会统计一个串的每个前缀出现次数,而这里只需要统计每个串出现的次数。
那么如何判断一个串是不是另一个串的前缀呢?这里就用到反向比较的方法了。
例如:字符串:abc,和字符串abcd。再Trie树中,abc对应的出现次数为1,在搜索abcd的过程中会发现abc出现过,即存在一个题目所述的“bug!”。
另外,思考一下,为什么不会因为找到自己而返回True,导致出现bug呢?
注意下在Trie树中存储字符串出现次数的方法,代码第33行存储一个字符串出现的次数时,是字符串遍历结束,最后一个字母所指向的存储位置,而在查询过程中代码第39行的那个“p”其实是字符串中倒数第二个字母所指向的存储位置,因此,是不会遍历到自己的。
此时就引发了一个思考,如果有两个串是一致的,那么该代码会引发一个什么答案呢?
尝试一下,就会发现,该样例不成为为题目中所述的“Bug!”,但该样例是否应该触发“Bug!”呢?严格来说是应该触发的,即自身属于自身的一个前缀,当出现两个人的密码一致时,是应该触发一个“Bug!”,但这份代码依然通过了所有测试样例,由此可见,后台应该没有考虑这种情况。
但如果需要处理这种情况,该如何应对呢?
也很简单,判断某个字符串是否出现过超过一次即可。
怎么判断呢?挨个遍历比较?O(n^2)的复杂度。
那有什么高效算法呢?前缀树呀,代码33行一旦该值超过1,就说明该串出现了两次,直接返回触发“Bug!”
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define MAX_N 2333430
#define MAX_C 26
struct Trie{
int *ch[MAX_N];
int tot;
int cnt[MAX_N];
Trie() {
tot = 0;
memset( ch, 0, sizeof(ch) );
memset( cnt, 0, sizeof(cnt) );
}
void insert( const char* str ) {
int p = 0;
for ( int i = 0 ; str[i] ; ++ i ) {
if ( ch[p] == NULL ) {
ch[p] = new int[MAX_C];
memset( ch[p], -1, sizeof(int)*MAX_C );
}
if ( ch[p][str[i]-'a'] == -1 ) {
ch[p][str[i]-'a'] = ++tot;
}
p = ch[p][str[i]-'a'];
}
cnt[p] ++;// 这里记录完整的串 而不是前缀
}
bool find( const char* str ) {
int p = 0;
for ( int i = 0 ; str[i] ; ++ i ) {
if ( cnt[p] != 0 ) return true;
if ( ch[p] == NULL ) return false;
if ( ch[p][str[i]-'a'] == -1 ) return false;
p = ch[p][str[i]-'a'];
}
return false;
}
};
char s[MAX_N][15];
Trie trie;
int main()
{
int n;
scanf( "%d", &n );
getchar();
bool ans = false;
for ( int i = 0 ; i < n ; ++ i ) {
scanf( "%s", s[i] );
getchar();
trie.insert(s[i]);
}
for ( int i = 0 ; i < n ; ++ i ) {
if ( trie.find(s[i]) ) {
ans = true;
break;
}
}
if ( ans ) {
puts("Bug!");
} else {
puts("Good Luck!");
}
return 0;
}