当前位置: 首页 > news >正文

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;
}

相关文章:

  • mysql-sql_mode参数类型
  • 知识表示方法之五:脚本表示法
  • 4.nRF52xx蓝牙学习(GPIOTE与外部中断)
  • 新增一种线性回归的增量学习框架,已更新31个模型!Matlab回归预测大合集又更新啦!
  • 【备赛】蓝桥杯实现多个LED联合控制
  • Java 21新特性实战:虚拟线程如何让吞吐量提升10倍?
  • WPS宏开发手册——附录
  • PostgreSQL迁移
  • vs中两个项目同在一个解决方案时,只生成一个的bug
  • Redis 渐进式rehash怎么判定rehash完成了?
  • Java基础 4.6
  • 算法专题(八):分治-归并排序
  • Cyber Weekly #50
  • TCPIP详解 卷1协议 一 概述
  • 【C/C++】打开转盘锁(leetcode T752)
  • Java EE期末总结(第四章)
  • VBA之Excel应用第四章第二节:单元格对象的行、列属性
  • Flask学习笔记 - 数据库
  • 网络安全应急响应-用户信息排查
  • 鸿蒙Arkts开发飞机大战小游戏,包含无敌模式,自动射弹,暂停和继续
  • 体育网站怎样做香功/什么推广平台好
  • 中国通信建设协会网站/天天广告联盟
  • 苹果手机怎么做微电影网站吗/百度关键词优化专家
  • dz论坛怎么做视频网站/百度官方优化指南
  • wordpress建立店铺/seo百度百科
  • 长沙网站制作的/网站首页关键词如何优化