2025.7.3 字典树1

2025.7.3 字典树(Trie)

为啥做这个题单?而且没做完

字典树的应用

查找已出现过的字符串

实现

构造

void add(string s){
	int p=0,ch;
	for(int i=0;i<s.size();i++){
		ch=s[i]-'a';
		if(!tr[p].mp[ch]){
			tr[p].mp[ch]=++cnt;//新的节点
		}
		p=tr[p].mp[ch];
	}
	tr[p].ending++;
}

查询

与构造相似

int query(string s){
	int p=0,ch;
	for(int i=0;i<s.size();i++){
		ch=s[i]-'a';
		if(!tr[p].mp[ch]){
            return -1;//查询失败
		}
		p=tr[p].mp[ch];
	}
	return tr[p].ending;
}

例题

P2536

题目描述

每个 DNA 片段都是由 ACTG 组成的序列。科学家们也总结出了 Samuel 星球上的“病毒模版片段”。一个模版片段是由 ACTG 的序列加上通配符 *? 来表示。其中 * 的意思是可以匹配上 0 个或任意多个字符,而 ? 的意思是匹配上任意一个字母。

如果一个 DNA 片段能够和“病毒模版片段”相匹配,那么这个 DNA 片段就是未知的病毒。

例如,假设 “病毒模版片段”为 A*G?C。DNA 片段:AGTCAGTGTC 都是未知的病毒,而 DNA 片段 AGTGC 则不是病毒。

由于,机器人搜集的这些 DNA 片段中除去病毒的其他部分都具有非常高的研究价值。所以科学家们希望能够分辨出其中哪些 DNA 片段不是病毒,并将不是病毒的 DNA 片段运回宇宙空间站继续进行研究。

科学家将这项任务交给了小联。现在请你为小联编写程序统计哪些 DNA片段不是病毒。

[!NOTE]

对于所有数据,0 < N (DNA片段个数) < 500
特别的:
每个 DNA 片段的长度不超过 500
“病毒模版片段”和 DNA 片段的长度都至少为 1

思路

  1. 对于 ? ,直接判断当前位置是否有指向ACTG 的路径。
  2. 对于 * ,可以当作空串跳过或当作 '?'+'*' 处理。即dfs(i,tr[p].next)dfs(i+1,tr[p].next)
  3. 对于ACTG ,向下搜索。
  4. 对当前情况进行标记,防止重复搜索。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,cnt=0,anss=0;
map<char,int> c;
string ans,s;
struct node{
	int mp[4],ending;
}tr[N];
bool vis[1005][N];
void add(string s){//Trie构造
	int p=0,ch;
	for(int i=0;i<s.size();i++){
		ch=c[s[i]];
		if(!tr[p].mp[ch]){
			tr[p].mp[ch]=++cnt;
		}
		p=tr[p].mp[ch];
	}
	tr[p].ending++;
}
void dfs(int i,int p){//病毒串位置i,Trie位置p
	if(i==ans.size()){
		anss+=tr[p].ending;
		tr[p].ending=0;//防止重复计算
		return ;
	}
	if(vis[i][p]){
		return ;
	}
	vis[i][p]=1;//标记处理
	if(ans[i]=='*'){
		dfs(i+1,p);//当作空串
		for(int j=0;j<4;j++){
			if(tr[p].mp[j]){
				dfs(i,tr[p].mp[j]);//当作'?'+'*'
			}
		}
		return ;
	}
	if(ans[i]=='?'){
		for(int j=0;j<4;j++){
			if(tr[p].mp[j]){
				dfs(i+1,tr[p].mp[j]);
			}
		}
		return ;
	}
	int ch=c[ans[i]];
	if(tr[p].mp[ch]){
		dfs(i+1,tr[p].mp[ch]);
	}
}
int main(){
	c['A']=0,c['C']=1,c['T']=2,c['G']=3;//初始化
	cin>>s;
	for(int i=0;i<s.size();i++){
		if(i>=1&&s[i]==s[i-1]&&s[i]=='*'){
			continue;
		}
		ans+=s[i];
	}
    //处理连续‘*’情况
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>s;
		add(s);
	}
	dfs(0,0);
	cout<<n-anss<<endl;//输出不是病毒的数量
	return 0;
}
posted @ 2025-07-05 19:32  liyuan2023  阅读(7)  评论(0)    收藏  举报