Trie树

统计前缀

在这里插入图片描述

#include<iostream>
#include<cstring>
using namespace std;
const int N = 1000005;//树里面总的结点数,最多100000个单词但是结点数肯定多于100000
int n,m;
int tree[N][27] = {0};//tree[i][j]存的是结点i的第j个儿子的节点号,用这样的方式连起来
int fa[N] = {0};//找父亲
bool f[N] = {0};//判断是否为一个完整单词的标记
int tot = 0;//记总的结点数
char ch[15] = {0};
int sizze[N] = {0};//从树根到i结点为前缀的单词总个数,size不能用,会CE的

void insert(char *str){
	int len = strlen(str);
	int rt = 0;
	for(int i=0;i<len;i++){
		int id = str[i]-'a';
		if(!tree[rt][id]) {
			tree[rt][id] = ++tot;
			fa[tot] = rt;
		}
		rt = tree[rt][id];
	}
	f[rt] = true;
	while(rt){
		sizze[rt]++;
		rt = fa[rt];//倒着加一遍……复杂度可能不太妙
	}
}

void find(char *str){
	int len = strlen(str);
	int rt = 0,ans = 0;
	for(int i=0;i<len;i++){
		int id = str[i]-'a';
		if(!tree[rt][id]){
			cout<<"0"<<endl;
			return ;
		}
		rt = tree[rt][id];
	}
	cout<<sizze[rt]<<endl;
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>ch;
		insert(ch);
	}
	cin>>m;
	for(int i=1;i<=m;i++){
		cin>>ch;
		find(ch);
	}
	return 0;
} 

插入删除查找

在这里插入图片描述

#include<iostream>
#include<cstring>
using namespace std;
const int N = 3000005;
int n;
int fa[N] = {0},sizze[N] = {0};
int tree[N][27] = {0};
bool word[N] = {0};
int tot = 0;
char ch1[10] = {0},ch2[31] = {0};

void _insert(char *str){
	int len = strlen(str);
	int rt = 0;
	for(int i=0;i<len;i++){
		int id = str[i]-'a';
		if(!tree[rt][id]){
			tree[rt][id] = ++tot;
			fa[tot] = rt;	
		} 
		rt = tree[rt][id];
	}
	word[rt] = true;
	while(rt){
		sizze[rt]++;
		rt = fa[rt];
	}
}

void _delete(char *str){
	int len = strlen(str);
	int rt = 0,idx;
	for(int i=0;i<len;i++){
		idx = str[i]-'a';
		if(!tree[rt][idx]) return;//呃呃呃呃没意识到这里可能根本找不到这样的前缀,以为数据都合法呢
		rt = tree[rt][idx];
	}
	while(rt){//注意呀……不一定是只删掉最后的那个结点,有的时候前面的也要删,因为说是删单词呀,是一整个一整个删掉的,只是看看具体在树上要删多少而已
		if(sizze[rt]!=sizze[fa[rt]]) {
			sizze[fa[rt]] -= sizze[rt];
			tree[fa[rt]][str[len-1]-'a'] = 0;
			break;
		}
		len--;
		rt = fa[rt];
	}
	
}

void _search(char *str){
	int len = strlen(str);
	int rt = 0;
	for(int i=0;i<len;i++){
		int id = str[i]-'a';
		if(!tree[rt][id]){
			cout<<"No"<<endl;
			return ; 
		}
		rt = tree[rt][id];
	}
	cout<<"Yes"<<endl;
	return ;
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>ch1>>ch2;
		if(ch1[0]=='i') _insert(ch2);
		if(ch1[0]=='d') _delete(ch2);
		if(ch1[0]=='s') _search(ch2);
	}
	return 0;
} 

树上KMP

在这里插入图片描述

在这里插入图片描述

#include<iostream>
#include<cstring>
using namespace std;
const int N = 1000005;
int tree[2*N][26] = {0};//开2e6+5?
int tot = 0,n;
bool flagg[N*2] = {0}; 
char t[N] = {0},s[N] = {0};
int fail[N] = {0};

void _insert(char *str){
	int len = strlen(str);
	int rt = 0;
	for(int i=0;i<len;i++){
		int id = str[i]-'a';
		if(!tree[rt][id]) {
			tree[rt][id] = ++tot;
		}	
		rt = tree[rt][id];
	}
	flagg[rt] = true;
}

// BFS to build the fail pointer on trie tree
// 如果已经求得了深度不高于结点t的各结点的fail指针,更新t的子节点的fail指针
// 需要额外注意回溯到根节点时的边界情况
void bfs(int u){
	int prev;
	for(int i=0;i<26;i++){
		if(!tree[u][i]) continue;
		int c = tree[u][i];
		prev = fail[u];
		while(tree[prev][i]==0 && prev != -1) prev = fail[prev];
		if(prev != -1) fail[c] = tree[prev][i];
	} 
	for(int i=0;i<26;i++){
		if(tree[u][i]) bfs(tree[u][i]);
	}
}

bool check(){
	int i = 0,j = 0;
	int len = strlen(t);
	while(flagg[i]!=true && j<len){
		if(i==-1){
			i = 0;
		}
		if(tree[i][t[j]-'a']!=0){
			i = tree[i][t[j]-'a'];
			j++;
		}
		else if(i==0){
			j++;
		}
		else{
			i = fail[i];
		}
	}
	if(flagg[i]) return true;
	else return false;
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>s;
		_insert(s);
	}
	fail[0] = -1;
	bfs(0);
	cin>>t;
	if(check())
		cout<<"valar morghulis"<<endl;//yes
	else cout<<"valar dohaeris"<<endl;//no
	return 0;
}

注意哈,这里写kmp还是跟一维匹配差不多的,一开始设置一个-1,作为没有匹配上的标记,处理fail数组的时候特别注意要关照好根节点。其他的和一维的类似,只是i++这个语句变成树上的操作而已~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值