【题目来源】
https://www.luogu.com.cn/problem/P2580
【题目背景】
XS中学化学竞赛组教练是一个酷爱炉石的人。 他会一边搓炉石一边点名以至于有一天他连续点到了某个同学两次,然后正好被路过的校长发现了然后就是一顿欧拉欧拉欧拉(详情请见已结束比赛 CON900)。
【题目描述】
这之后校长任命你为特派探员,每天记录他的点名。校长会提供化学竞赛学生的人数和名单,而你需要告诉校长他有没有点错名。(为什么不直接不让他玩炉石。)
【输入格式】
第一行一个整数 n,表示班上人数。
接下来 n 行,每行一个字符串表示其名字(互不相同,且只含小写字母,长度不超过 50)。
第 n+2 行一个整数 m,表示教练报的名字个数。
接下来 m 行,每行一个字符串表示教练报的名字(只含小写字母,且长度不超过 50)。
【输出格式】
对于每个教练报的名字,输出一行。
如果该名字正确且是第一次出现,输出 OK,如果该名字错误,输出 WRONG,如果该名字正确但不是第一次出现,输出 REPEAT。
【输入样例】
5
a
b
c
ad
acd
3
a
a
e
【输出样例】
OK
REPEAT
WRONG
【说明/提示】
对于 40% 的数据,n≤1000,m≤2000。
对于 70% 的数据,n≤10^4,m≤2×10^4。
对于 100% 的数据,n≤10^4,m≤10^5。
【算法分析】
● 字典树(Trie 树)是一种用于快速检索的多叉树结构。在检索过程中,充分利用字符串的公共前缀降低查询的时间开销,最大限度地减少无谓的字符串比较,从而达到快速检索的目的。
● 除根结点外,字典树的每个结点只包含一个字符。
● 字典树中每个结点都有一个序号。字典树的根结点为空结点,序号为 0。
从代码层面来讲,本例中的 sn[p][u] 表示序号为 p 的结点的子结点的序号。cnt[p] 表示以序号为 p 的结点结尾的字符串个数。由于本例规定“名字只含小写字母”,故任意结点最多有 26 个分支,进而可以理解代码 sn[p][u] 中的 u 为由字符 a~z 映射而得的 0~25。
● 不同字典树问题的代码有 insert() 及 query() 两个核心函数。其 insert() 函数代码大多都一样,而 query() 函数会随问题多变。
● 字典树在实现时,会对每个字符串的结尾设置标记。
字符串“big、do、dog、dob、date、fat”的字典树(Trie 树)如下所示:
图中绿底儿的结点,表示字符串的末尾。
● 字典树常用于词频统计、前缀匹配、字符串检索、字符串排序等。
● STL map:https://cplusplus.com/reference/map/
#include <bits/stdc++.h>
using namespace std;
int main() {
map<char,int> imap;
imap['a']=10;
imap['b']=20;
imap['c']=30;
while(!imap.empty()) {
cout<<imap.begin()->first<<" => "<<imap.begin()->second<<endl;
imap.erase(imap.begin());
}
return 0;
}
/*
out:
a => 10
b => 20
c => 30
*/
【算法代码:Trie 实现】
● 本 Trie 实现代码中的 maxn 需设为 400005 或更大,而不是依题设的字面数据范围 100005,否则会 RE(Runtime Error)→ Received signal 11: Segmentation fault with invalid memory reference.
● 据说,将 Trie 实现代码中的 <<endl; 换成 '\n'; 能提高 8 倍速。
#include <bits/stdc++.h>
using namespace std;
const int maxn=4e5+5;
int sn[maxn][26]; //sn[p][u] indicates the serial number
int cnt[maxn]; //number of words ending in the current node
int flag[maxn];
string str;
int idx;
void insert(string s) {
int p=0; //root=0
for(int i=0; i<s.size(); i++) {
int u=s[i]-'a'; //a~z are mapped to 0~25
if(!sn[p][u]) sn[p][u]=++idx;
p=sn[p][u];
}
cnt[p]++;
flag[p]=1;
}
void query(string s) {
int p=0; //root=0
for(int i=0; i<s.size(); i++) {
int u=s[i]-'a'; //a~z are mapped to 0~25
if(!sn[p][u]) {
cout<<"WRONG"<<endl;
return;
}
p=sn[p][u];
}
if(flag[p]==0) {
cout<<"WRONG"<<endl;
return;
} else if(flag[p]==1) {
cout<<"OK"<<endl;
flag[p]++;
} else cout<<"REPEAT"<<endl;
}
int main() {
int n,m;
cin>>n;
for(int i=1; i<=n; i++) {
cin>>str;
insert(str);
}
cin>>m;
for(int i=1; i<=m; i++) {
cin>>str;
query(str);
}
return 0;
}
/*
in:
5
a
b
c
ad
acd
3
a
a
e
out:
OK
REPEAT
WRONG
*/
【算法代码:STL map 实现】
#include <bits/stdc++.h>
using namespace std;
map<string,int> mp;
string s;
int n,m;
int main() {
cin>>n;
while(n--) {
cin>>s;
mp[s]=1;
}
cin>>m;
while(m--) {
cin>>s;
if(mp[s]==1) {
cout<<"OK"<<endl;
mp[s]=2;
} else if(mp[s]==2) cout<<"REPEAT"<<endl;
else cout<<"WRONG"<<endl;
}
return 0;
}
/*
in:
5
a
b
c
ad
acd
3
a
a
e
out:
OK
REPEAT
WRONG
*/
【参考文献】
https://www.cnblogs.com/v-vip/p/8849036.html
https://www.luogu.com.cn/problem/solution/P2580
https://blog.csdn.net/hnjzsyjyj/article/details/138599488
https://blog.csdn.net/hnjzsyjyj/article/details/138631374