应用一:单词匹配
给定 s 1 , s 2 , . . . , s n s_1,s_2,...,s_n s1,s2,...,sn 字符串,有若干次查询,对于每次查询,输入一个字符串 T T T,问是否存在于 s s s 中(要完全对应于其中一个 s s s 才算存在)。
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1000 + 10;
bool tail[maxn]; //尾部标记
int trie[maxn][26], tot;
void insert(string str){
int p = 1;
for(int i = 0; i < str.length(); i++){
int ch = str[i] - 'a';
if(trie[p][ch] == 0) trie[p][ch] = ++tot;
p = trie[p][ch];
}
tail[p] = true;
}
bool search(string str){
int p = 1;
for(int i = 0; i < str.length(); i++){
p = trie[p][str[i] - 'a'];
if(p == 0) return false;
}
return tail[p];
}
inline void init(){
memset(tail,false,sizeof tail);
tot = 1;
}
int main()
{
init();
int n;
cin>>n;
string s;
for(int i=0;i<n;i++){
cin>>s;
insert(s);
}
for(int i=0;i<n;i++){
cin>>s;
cout<<search(s)<<endl;
}
return 0;
}
应用二:前缀匹配
给定
s
1
,
s
2
,
.
.
.
,
s
n
s_1,s_2,...,s_n
s1,s2,...,sn 字符串,有若干次查询,对于每次查询,输入一个字符串
T
T
T,问
T
T
T 是否对应其中一个
s
s
s 的前缀(完全对应可行)。
p
s
ps
ps:相对于应用一,去掉尾部标记。
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1000 + 10;
int trie[maxn][26], tot;
void insert(string str){
int p = 1;
for(int i = 0; i < str.length(); i++){
int ch = str[i] - 'a';
if(trie[p][ch] == 0) trie[p][ch] = ++tot;
p = trie[p][ch];
}
}
bool search(string str){
int p = 1;
for(int i = 0; i < str.length(); i++){
p = trie[p][str[i] - 'a'];
if(p == 0) return false;
}
return true;
}
inline void init(){
tot = 1;
}
int main()
{
init();
int n;
cin>>n;
string s;
for(int i=0;i<n;i++){
cin>>s;
insert(s);
}
for(int i=0;i<n;i++){
cin>>s;
cout<<search(s)<<endl;
}
return 0;
}
应用三:单词统计
给定
N
N
N 个字符串
S
1
,
S
2
.
.
.
S
N
S_1,S_2...S_N
S1,S2...SN,接下来进行M次询问,每次询问给定一个字符串
T
T
T,求
S
1
~
S
N
S_1~S_N
S1~SN 中有多少个字符串是
T
T
T 的前缀。
题目来源:CH1601
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
int n,m;
int trie[maxn][26],tot;
ll tail[maxn];
void insert(string str){
int p = 1;
for(int i = 0; i < str.length(); i ++){
int ch = str[i] - 'a';
if(trie[p][ch] == 0) trie[p][ch] = ++tot;
p = trie[p][ch];
}
tail[p]++;
}
ll search(string str){
int p = 1;
ll sum = 0;
for(int i = 0; i < str.length(); i++){
p = trie[p][str[i] - 'a'];
if(p == 0) break;
sum += tail[p];
}
return sum;
}
inline void init(){
tot = 1;
memset(trie, 0, sizeof trie);
memset(tail, 0, sizeof tail);
}
int main()
{
cin>>n>>m;
string s,t;
init();
for(int i=0;i<n;i++){
cin>>s;
insert(s);
}
for(int i=0;i<m;i++){
cin>>t;
cout<<search(t)<<endl;
}
return 0;
}
应用四:前缀统计
老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
题目来源:poj1251
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int maxn = 1e6 + 10;
int trie[maxn][26], tot;
char s[maxn];
int tail[maxn];
void init(){
tot = 1;
memset(tail,0,sizeof tail);
}
void insert(char* s){
int p = 1,len = strlen(s);
for(int i = 0;i < len; i++){
int ch = s[i] - 'a';
if(!trie[p][ch]) trie[p][ch] = ++tot;
p = trie[p][ch];
tail[p]++;
}
}
int search(char* s){
int p = 1, len = strlen(s);
for(int i = 0; i < len; i++){
p = trie[p][s[i]-'a'];
if(p == 0) return 0;
}
return tail[p];
}
int main()
{
init();
while(gets(s),s[0]){
insert(s);
}
while(gets(s)){
cout<<search(s)<<endl;
}
return 0;
}
应用五:Phone List
题目描述:输出
n
n
n 个电话号码,若其中有两个号码存在谁是谁的前缀,则输出NO
,否则输出YES
题目来源:poj3630
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int maxn = 10000 + 10;
int n;
int trie[maxn * 10 + 5][10], tot;
bool tail[maxn * 10 + 5];
void init(){
tot = 1;
memset(tail, false, sizeof tail);
memset(trie,0,sizeof trie);
}
void insert(string s){
int p = 1;
for(int i = 0; i < s.length(); i++){
int ch = s[i] - '0';
if(!trie[p][ch]) trie[p][ch] = ++tot;
p = trie[p][ch];
}
tail[p] = true;
}
int search(string s){
int p = 1;
for(int i = 0; i < s.length(); i++){
p = trie[p][s[i]-'0'];
if(p == 0) return false;
if(tail[p] == true) return true;
}
return true;
}
int main()
{
int t,flag;
string s;
cin>>t;
while(t--){
cin>>n;
flag = 0;
init();
for(int i = 0; i < n; i++){
cin>>s;
if(!flag){ //如果已经确定是NO,就不用再查了
if(search(s) == true){
flag = 1;
}
insert(s);
}
}
if(flag == 1) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return 0;
}
应用六:最大XOR
数对
题目描述:在给定的
N
N
N 个整数
A
1
,
A
2
…
…
A
N
A_1,A_2……A_N
A1,A2……AN中选出两个进行xor
运算,得到的结果最大是多少?
解题思路:将每位
A
i
A_i
Ai 的二进制存入字典树中。然后对每一位
A
i
A_i
Ai,在字典树中找。
题目来源:CH1602
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 2e6 + 10;
int n;
int trie[maxn][2], tot;
int tail[maxn];
void insert(int x){
int p = 1;
for(int i = 30; i >= 0; i--){
int ch = (x>>i)&1;
if(!trie[p][ch]) trie[p][ch] = ++tot;
p = trie[p][ch];
}
tail[p] = x;
}
int search(int x){
int p = 1;
for(int i = 30; i >= 0; i--){
int ch = (x>>i)&1;
if(trie[p][ch^1])
p = trie[p][ch^1];
else p = trie[p][ch];
}
return tail[p]^x;
}
void init(){
tot = 1;
}
int main()
{
cin>>n;
init();
int a,res=0;
for(int i = 1; i <= n; i++){
cin>>a;
res = max(res, search(a)); //每次在1~i-1中找到最大xor
insert(a);
}
cout <<res<<endl;
return 0;
}
应用七:The xor-longest Path
题目描述:一棵有
n
n
n 个节点的树,每条边都有一个权值。在树上找两个节点
x
x
x 和
y
y
y,使得这条路径上的值的异或和最大,输出异或和的最大值。
解题思路:用
d
[
x
]
d[x]
d[x] 保存
x
x
x 节点到根节点路径上所有数的异或和,然后问题转化成从
d
d
d 数组中选区两个数,使得这两个数的异或和最大。因为
x
x
x 节点和
y
y
y 节点的
L
C
A
LCA
LCA 到根节点的路径上的异或和抵消了。
题目来源:poj3764
一开始
t
a
i
l
tail
tail 数组开小了导致总是没有过,也没有找出问题,谢谢一个在校同伴救了我
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
struct edge{
int to, w, next;
};
const int maxn = 1e5 + 100;
int n;
int trie[maxn*31][2], tot;
int head[maxn], cnt;
int tail[maxn*31];
edge e[maxn*2];
int d[maxn];
void init(){
cnt = 0;
tot = 1;
memset(head, -1, sizeof head);
memset(trie, 0, sizeof trie);
memset(d, 0, sizeof d);
memset(tail,0,sizeof tail);
}
void addedge(int u, int v, int w){
e[cnt].to = v; e[cnt].w = w;
e[cnt].next = head[u]; head[u] = cnt++;
}
void dfs(int u, int fa, int val){
d[u] = val;
for(int i = head[u]; ~i; i = e[i].next){
if(e[i].to != fa)
dfs(e[i].to, u, e[i].w ^ val);
}
}
void insert(int x){
int p = 1;
for(int i = 30; i >= 0; i--){
int ch = (x>>i)&1;
if(!trie[p][ch]) trie[p][ch] = ++tot;
p = trie[p][ch];
}
tail[p] = x;
}
int search(int x){
int p = 1;
for(int i = 30; i >= 0; i--){
int ch = (x>>i)&1;
if(trie[p][ch^1])
p = trie[p][ch^1];
else p = trie[p][ch];
}
return tail[p] ^ x;
}
int main()
{
int u, v, w;
while(cin>>n){
init();
for(int i = 1; i < n; i++){
scanf("%d%d%d",&u,&v,&w);
addedge(u, v, w);
addedge(v, u, w);
}
dfs(0, 0, 0);
int res = 0;
for (int i = 0; i < n; ++i)
{
res = max(res, search(d[i]));
insert(d[i]);
}
cout<<res<<endl;
}
return 0;
}