Living-Dream 系列笔记 第60期

Posted on 2024-06-20 17:49  _XOFqwq  阅读(4)  评论(0编辑  收藏  举报

\(\mathcal{TRIE}\):用于存储和查询字符串的树形结构,相同前缀的字符串共用节点,每个节点存储一个字符。
操作:

  • insert:单次 \(O(len)\)

  • search:单次 \(O(len)\)

性质 \(1\)若一个字符串 \(T\) 作为前缀,则包含 \(T\) 的所有字符串的“终止节点”一定在以 \(T\) 的“终止节点”为根的子树内。

关于空间:

第一维的上限一般为字符串总长度,也可以是 \(\text{字符种类数}^{\text{最长的字符串长度}}\),在两者中取 \(\min\) 即可。

T1

vis 改为计数器,到末尾时还是标记为 \(1\)(因为要判错误),search 中判错误后计数器加一,若为 \(2\)OK,否则 REAPEAT

code
#include<bits/stdc++.h>
using namespace std;

const int N=5e6+5,M=31;
int n,m;
int tot,trie[N][M];
int vis[N];

void insert(string s){
	int cur=0;
	for(int i=0;i<s.size();i++){
		if(!trie[cur][s[i]-'a'])
			trie[cur][s[i]-'a']=++tot;
		cur=trie[cur][s[i]-'a'];
	}
	vis[cur]=1;
}
string search(string s){
	int cur=0;
	for(int i=0;i<s.size();i++){
		if(!trie[cur][s[i]-'a'])
			return "WRONG\n";
		cur=trie[cur][s[i]-'a'];
	}
	if(!vis[cur]) return "WRONG\n";
	vis[cur]++;
	if(vis[cur]==2) return "OK\n";
	return "REPEAT\n";
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		string s; cin>>s,insert(s);
	}
	cin>>m;
	for(int i=1;i<=m;i++){
		string s; cin>>s,cout<<search(s);
	}
	return 0;
}

T2

运用性质 \(1\),我们维护 \(cnt_i\) 表示节点 \(i\) 的被经过次数,对于 \(s\) 建 trie,把 \(t\) 丢到里面匹配,答案即为 \(cnt_{j}\)\(j\) 即为匹配的最终节点),此时 vis 无用处(\(t\) 不一定在 \(s\) 中,因此\(end\) 不一定有标记)。注意多测清空不能用 memset

code
#include<bits/stdc++.h>
using namespace std;

const int N=3e6+5,M=80;
int t,n,q;
int tot,trie[N][M];
//bool vis[N];
int cnt[N];

int g(char c){
	return c-'0';
}
void insert(string s){
	int cur=0;
	for(int i=0;i<s.size();i++){
		if(!trie[cur][g(s[i])])
			trie[cur][g(s[i])]=++tot;
		cur=trie[cur][g(s[i])],cnt[cur]++;
	}
	//vis[cur]=1;
}
int search(string s){
	int cur=0;
	for(int i=0;i<s.size();i++){
		if(!trie[cur][g(s[i])])
			return 0;
		cur=trie[cur][g(s[i])];
	}
	//if(!vis[cur]) return 0;
	return cnt[cur];
}

int main(){
	cin>>t;
	while(t--){
		for(int i=0;i<=tot;i++){
			cnt[i]=0;
			for(int j=0;j<M;j++) trie[i][j]=0;
		}
		tot=0;
		cin>>n>>q;
		for(int i=1;i<=n;i++){
			string s; cin>>s,insert(s);
		}
		for(int i=1;i<=q;i++){
			string s; cin>>s,cout<<search(s)<<'\n';
		}
	}
	return 0;
}

作业 T1

首先我们对信息建 trie。注意到本题对于每一条暗号,它作为信息的前缀 / 信息作为它的前缀 均可进行匹配。前者运用性质 \(1\) 即可;后者我们维护一个 \(end_i\) 表示节点 \(i\) 作为一个信息的最终节点的次数,insert 中每插入一个单词就加一,search 中令答案不断加上经过节点的 \(end_i\)(求出信息作为它的前缀的个数),若走不下去了就直接返回答案(“这个前缀长度必须等于暗号和那条信息长度的较小者”),否则令答案 \(+ \ cnt_{j} - end_{j}\) 再输出(\(-\ end_{j}\) 是因为前面加过了一遍)。

code
#include<bits/stdc++.h>
using namespace std;

const int N=5e5+5,M=55;
int m,n;
int tot,trie[N][M];
int cnt[N],vis[N];

void insert(string s){
	int cur=0;
	for(int i=0;i<s.size();i++){
		if(!trie[cur][s[i]])
			trie[cur][s[i]]=++tot;
		cur=trie[cur][s[i]],cnt[cur]++;
	}
	vis[cur]++;
}
int search(string s){
	int cur=0,ans=0;
	for(int i=0;i<s.size();i++){
		if(!trie[cur][s[i]])
			return ans;
		cur=trie[cur][s[i]],ans+=vis[cur];
	}
	return ans+cnt[cur]-vis[cur];
}

int main(){
	cin>>m>>n;
	for(int i=1,b;i<=m;i++){
		cin>>b; string s; s.resize(b);
		for(int j=0;j<b;j++) cin>>s[j];
		insert(s);
	}
	for(int i=1,c;i<=n;i++){
		cin>>c; string s; s.resize(c);
		for(int j=0;j<c;j++) cin>>s[j];
		cout<<search(s)<<'\n';
	}
	return 0;
}

作业 T2

至今未卡过(90 pts)。。。

考虑对字典建 trie。把文章扔进去匹配(dfs),匹配上一个单词就更新答案即可。注意搜过了的位置不用再搜。

code
#include<bits/stdc++.h>
using namespace std;

const int N=2e6+5,M=1e3+5;
int n,m,ans;
string s;
bool chk[N];
int vis[N];
int tot,trie[M][31];

inline void read(string &s) {
    s = "";
    char c = getchar();
    while (c < 'a' || c > 'z') c = getchar();
    while (c >= 'a' && c <= 'z')
        s += c, c = getchar();
}

inline void write(int x) {
    if (x < 10) return putchar(x + '0'), void();
    write(x / 10);
    putchar(x % 10 + '0');
}

void ins(string s){
	int cur=0;
	for(int i=0;i<s.size();i++){
		if(!trie[cur][s[i]-'a'])
			trie[cur][s[i]-'a']=++tot;
		cur=trie[cur][s[i]-'a'];
	}
	vis[cur]++;
}
void sch(int cur,int len){
	if(chk[cur]) return; chk[cur]=1;
	ans=max(ans,cur); int x=cur,now=0;
	while(x<len){
		if(trie[now][s[x]-'a']){
			now=trie[now][s[x]-'a'],x++;
			if(vis[now]) sch(x,len);
		}
		else break;
	}
}

int main(){
	//ios::sync_with_stdio(0);
	//cin.tie(nullptr),cout.tie(nullptr);
 	cin>>n>>m;
	for(int i=1;i<=n;i++) read(s),ins(s);
	for(int i=1;i<=m;i++){
  		memset(chk,0,sizeof(chk)),read(s);
		ans=0,sch(0,s.size());
		write(ans),putchar('\n');
	}
	return 0;
}