Tire树(字典树)
理论

 上图是一棵Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。从上图可以归纳出Trie树的基本性质:
- 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
- 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符互不相同。
- 从第一字符开始有连续重复的字符只占用一个节点,比如上面的to,和ten,中重复的单词t只占用了一个节点。
模板
//节点
struct Node {
	//每个节点有26个子节点
	Node* son[26]{};
	//末尾标记,可用于区分前缀字符串和完整字符串
	bool end = false;
};
class Tire {
	Node* root = new Node();
	int find(string s) {
		Node* cur = root;
		for (char c : s) {
			int t = c - 'a';
			if (cur->son[t] == nullptr) {
				//没找到返回0
				return 0;
			}
			cur = cur->son[t];
		}
		//找到完整的返回2,前缀字符串返回1
		return cur->end ? 2 : 1;
	}
public:
    //插入字符串
    void insert(string word) {
        Node* cur=root;
        for(char c:word){
            c-='a';
            if(cur->son[c]==nullptr){
                cur->son[c]=new Node();
            }
            cur=cur->son[c];
        }
        cur->end=true;
    }
    //查询完整字符串
    bool search(string word) {
        return find(word)==2;
    }
    //查询前缀字符串
    bool startsWith(string prefix) {
        return find(prefix)!=0;
    }
};
例题
例题1
爱找事的小Z
 题目描述:
 小 Z 同学非常喜欢找事,现在有很多名为“事”的字符串,现在小 Z 想要找“事”,请你帮助他判断,他今天是否找了两件相同的事。
 输入描述
 输入第一行包含一个整数 n,表示小 Z 今天找了多少事。
 接下来 n行分别表示 n 件事。
 事的输入量不超过 1e3,每个"事"字符串的长度不超过 1000,且所有字母均为小写。
 输出描述
 若有相同的事输出1,否则输出0。
 输入输出样例
 示例1
 输入
12
acd
acd
asdfsdf
asd
f
saf
asdf
sfasdfs
f
asdf
asf
asdfs
输出
1
示例2
 输入
21
adfasdfaasdf
asdfsfas
fa
sdfasd
fsd
fs
a
sda
fsd
afasd
f
sda
f
asdf
as
df
sda
gggggas
dfdsfe
def
a
输出
1
示例
 输入
9
asdfasdfa
asdfb
asdc
fasd
fde
sadf
fg
hsadfasdfasdg
iggsadffsa
输出
0
#include<bits/stdc++.h>
using namespace std;
//节点
struct Node {
	//每个节点有26个子节点
	Node* son[26]{};
	//末尾标记,可用于区分前缀字符串和完整字符串
	bool end = false;
};
class Tire {
	Node* root = new Node();
	int find(string s) {
		Node* cur = root;
		for (char c : s) {
			int t = c - 'a';
			if (cur->son[t] == nullptr) {
				//没找到返回0
				return 0;
			}
			cur = cur->son[t];
		}
		//找到完整的返回2,前缀字符串返回1
		return cur->end ? 2 : 1;
	}
public:
	//插入字符串
	void intsert(string s) {
		Node* cur = root;
		for (char c : s) {
			int t = c - 'a';
			if (cur->son[t] == nullptr) {
				cur->son[t] = new Node();
			}
			cur = cur->son[t];
		}
		cur->end = true;
	}
	//查询字符串
	bool  check(string s) {
		return find(s) == 2;
	}
};
int main() {
	Tire tire;
	int n;
	cin >> n;
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		string temp;
		cin >> temp;
		if (!tire.check(temp)) {
			tire.intsert(temp);
		}
		else {
			ans++;
		}
	}
	if (ans) {
		cout << 1;
	}
	else {
		cout << 0;
	}
	return 0;
}
例题2
小蓝的神秘图书馆
 问题描述
 小蓝是图书馆的管理员,他负责管理图书馆的所有书籍。图书馆有 N本书,每本书都有名字,分别为 S1,S2,…,SN。
 图书馆的读者们经常来询问小蓝,他们会给小蓝一个字符串 T,希望小蓝能告诉他们,图书馆里有多少本书的名字是以 T的前缀开头的。小蓝需要回答他们 M次这样的询问。
 现在,小蓝需要你的帮助。你能帮助小蓝解决这个问题,从而提升图书馆的服务质量吗?
 输入格式
 第一行输入两个整数 N 和 M(1≤N,M≤1e4)。
 接下来 N 行,每行输入一个字符串 Si,表示图书馆中的一本书的名字。
 接下来 M行,每行一个字符串 T,表示读者的询问。
 输入字符串的总长度不超过 2×1e5,且仅包含小写字母。
 输出格式
 对于每个询问,输出一个整数,表示图书馆中以字符串 T开头的书的数量。
 每个答案占一行。
 样例输入
5 2
ababc
ababd
aba
ab
a
abab
ccc
样例输出
2
0
#include<bits/stdc++.h>
using namespace std;
struct Node {
        Node* son[26];
        int end = 0;
};
class Tire {
        Node* root = new Node();
        int dfs(Node* cur) {
                int res = 0;
                if (cur == nullptr) {
                        return res;
                }
                if (cur->end) {
                        res += cur->end;
                }
                for (int i = 0; i < 26; i++) {
                        if (cur->son[i]) {
                                res+=dfs(cur->son[i]);
                        }
                }
                return res;
        }
public:
        void insert(string s) {
                Node* cur = root;
                for (char c : s) {
                        c = c - 'a';
                        if (cur->son[c] == nullptr) {
                                cur->son[c] = new Node();
                        }
                        cur = cur->son[c];
                }
                cur->end++;
        }
        int find(string s) {
                Node* cur = root;
                for (char c : s) {
                        c = c - 'a';
                        if (cur->son[c] == nullptr) {
                                return 0;
                        }
                        cur = cur->son[c];
                }
                return dfs(cur);
        }
};
int main() {
        Tire tire;
        int n, m;
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
                string s;
                cin >> s;
                tire.insert(s);
        }
        for (int i = 1; i <= m; i++) {
                string s;
                cin >> s;
                cout << tire.find(s) << '\n';
        }
        return 0;
}
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 2e5 + 10;
string str[N],s[N];
ll n = 0, m = 0;
ll trie[N][27], cnt[N];
ll idx = 2;
void insert(string a){
    ll p = 1;
    ll k = a.length() - 1;
    for (ll i = 1; i <= k; i++)
    {
        if (!trie[p][a[i] - '0']) trie[p][a[i] - '0'] = idx++;
        p = trie[p][a[i] - '0'];
        cnt[p]++;
    }
}
ll query(string a){
    ll p = 1;
    ll k = a.length() - 1;
    ll ans = 0x3f3f3f;
    for (ll i = 1; i <= k; i++)
    {
        p = trie[p][a[i] - '0'];
        ans = min(ans, cnt[p]);
    }
    return ans;
}
int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> m;
    for (ll i = 1; i <= n; i++)
    {
        cin >> str[i];
        str[i] = '0' + str[i];
        insert(str[i]);
    }
    for (ll i = 1; i <= m; i++)
    {
        cin >> s[i];
        s[i] = '0' + s[i];
        cout << query(s[i]) << endl;
    }
    return 0;
}
01Tire
理论

 x >> i & 1
 (010)>>30 → (000)&(001) → 0
 ……
 (010)>>2 → (000)&(001) → 0
 (010)>>1 → (001)&(001) → 1
 (010)>>0 → (010)&(001) → 0
1 << i
 (001)<<2 → (100) → 4
 (001)<<1 → (010) → 2
 (001)<<0 → (001) → 1
res |= (1<< i)
 (010)^(101) → (111) → 4+2+1=7
 (011)^(101) → (110) → 4+2+0=6
 (101)^(010) → (111) → 4+2+1=7
 (111)^(010) → (101) → 4+0+1=5
模板
const int N = 1e5 + 10;
int son[32 * N][2], tot = 1;
//插入
void insert(int x)
{
    int o = 1;
    for (int i = 30; i >= 0; --i)
    {
        int y = x >> i & 1;
        if (!son[o][y])
            son[o][y] = ++tot;
        o = son[o][y];
    }
}
//查询
int query(int x)
{
    int o = 1, res = 0;
    for (int i = 30; i >= 0; --i)
    {
        int y = x >> i & 1;
        //找相反方向的,0找1,1找0,异或后为1,大小最大
        if (son[o][!y])
            o = son[o][!y], res |= (1ll << i);
        //如果该方向没有,则按原有方向走
        else
            o = son[o][y];
    }
    return res;
}
例题
XOR最大值
 问题描述
 给定一个整数数组 arr 和一个整数 q 表示查询的数量。接下来的 q行,每行给出一个整数 x。对于每个 x,你需要找到 arr 中的一个数 y使得 xXORy的值最大,然后输出这个最大值。
 输入格式
 第一行包含一个整数 n,表示数组 arr 的大小。
 第二行包含 n个整数,分别是 arr的元素。(0≤arr[i]≤1e9)。
 第三行包含一个整数 q,表示查询的数量。
 接下来的 q行,每行包含一个整数 x。(0≤x≤1e9)。
 输出格式
 对于每个查询输出一行,表示 xXORy 的最大值。
 样例输入
5
3 5 7 10 12
3
3
7
10
样例输出
15
13
15
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int son[32 * N][2], tot = 1;
void insert(int x)
{
    int o = 1;
    for (int i = 30; i >= 0; --i)
    {
        int y = x >> i & 1;
        if (!son[o][y])
            son[o][y] = ++tot;
        o = son[o][y];
    }
}
int query(int x)
{
    int o = 1, res = 0;
    for (int i = 30; i >= 0; --i)
    {
        int y = x >> i & 1;
        if (son[o][!y])
            o = son[o][!y], res |= (1ll << i);
        else
            o = son[o][y];
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        int x;
        cin >> x;
        insert(x);
    }
    int q;
    cin >> q;
    while (q--)
    {
        int x;
        cin >> x;
        cout << query(x) << "\n";
    }
}
#include<bits/stdc++.h>
using namespace std;
struct Node {
	Node* son[2]{};
};
class Tire {
	Node* root = new Node();
public:
	void insert(int x) {
		Node* cur = root;
		for (int i = 30; i >= 0; i--) {
			int y = x >> i & 1;
			if (!cur->son[y]) {
				cur->son[y] = new Node();
			}
			cur = cur->son[y];
		}
	}
	int query(int x) {
		Node* cur = root;
		int res = 0;
		for (int i = 30; i >= 0; i--) {
			int y = x >> i & 1;
			if (cur->son[!y]) {
				res = res + (1 << i);
				cur = cur->son[!y];
			}
			else {
				cur = cur->son[y];
			}
		}
		return res;
	}
};
int main() {
	Tire tire;
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i)
	{
		int x;
		cin >> x;
		tire.insert(x);
	}
	int q;
	cin >> q;
	while (q--)
	{
		int x;
		cin >> x;
		cout << tire.query(x) << "\n";
	}
	return 0;
}
