Trie树 字典树-学习笔记

对于字符串匹配
KMP很好的解决了以一个文本串匹配一个模板串的问题
但如果模板串有多个呢
这是KMP不再适用
于是又有了一个新的数据结构——字典树Trie

字典树可以将多个单词压缩到一棵树上
这样便解决了对于一个文本串要匹配多个模板串时
要重复匹配相同前缀的弊端


假设现在有 h i s , m y , h e y , h e his,my,hey,he his,my,hey,he四个模式串
将他们建成字典树后就会长成这样
在这里插入图片描述

如图所示
字典树的每条边储存了一个字符
这样每条从根到红色节点的路径上的字符就各自组成了一个单词
但特别的,根节点是不表示字符或单词的!!!

虽然在字典树的定义中存字符的是边
但实际操作中为了方便我们还是会以该边下面的节点来表示字符

struct node
{
    node* nxt[26];//对应下一层字母的指针
    bool judge;//是否是上述的红色节点
    node()
    {
		judge=false;
		for(int i=0;i<26;i++)
		nxt[i]=NULL;
	}
};
node* rt=new node();//初始根结点

Trie–插入
void ins(char ss[])
{
    int len=strlen(ss);
    node* p=rt;
    for(int i=0;i<len;i++)
    {
        int num=ss[i]-'a';//找到下一层结点
        if(p->nxt[num]==NULL)
        {
            node* k=new node();
            p->nxt[num]=k;
        }//如果该节点不存在则创建新结点,否则继续迭代插入
        p=p->nxt[num];
    }
    p->judge=true;//单词插入完毕,标记该节点
}

Trie–匹配

查询给定的字符串是否在模式串中出现过

bool find(char ss[])
{
    int len=strlen(ss);
    node* p=rt;
    for(int i=0;i<len;i++)
    {
        int num=ss[i]-'a';
        p=p->nxt[num];
        if(p==NULL) return false;
        //如查找过程中有结点不存在,则匹配失败
    }
    if(p->judge)return true;
    //遍历完文本串,若该接点被标记,则查着成功
    else return false;//否则查找失败
}

来一道果题练练手

洛谷P2580 于是他错误的点名开始了
题目描述

这之后校长任命你为特派探员,每天记录他的点名。校长会提供化学竞赛学生的人数和名单,而你需要告诉校长他有没有点错名。

输入格式:

第一行一个整数 n,表示班上人数。接下来 n 行,每行一个字符串表示其名字(互不相同,且只含小写字母,长度不超过 50)。第 n+2 行一个整数 m,表示教练报的名字。接下来 m 行,每行一个字符串表示教练报的名字(只含小写字母,且长度不超过 50)。

输出格式:

对于每个教练报的名字,输出一行。如果该名字正确且是第一次出现,输出“OK”,如果该名字错误,输出“WRONG”,如果该名字正确但不是第一次出现,输出“REPEAT”。(均不加引号)

说明

对于 40%的数据,n≤1000,m≤2000;
对于 70%的数据,n≤10000,m≤20000;
对于 100%的数据, n≤10000,m≤100000。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

void print(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9)print(x/10);
    putchar(x%10+'0');
}

struct node
{
    node* nxt[26];
    bool judge,rem;
    node(){judge=rem=false;for(int i=0;i<26;i++)nxt[i]=NULL;}
};
node* rt=new node();
int n,m;

void ins(char ss[])
{
    int len=strlen(ss);
    node* p=rt;
    for(int i=0;i<len;i++)
    {
        int num=ss[i]-'a';
        if(p->nxt[num]==NULL)
        {
            node* k=new node();
            p->nxt[num]=k;
        }
        p=p->nxt[num];
    }
    p->judge=true;
}

int find(char ss[])
{
    int len=strlen(ss);
    node* p=rt;
    for(int i=0;i<len;i++)
    {
        int num=ss[i]-'a';
        p=p->nxt[num];
        if(p==NULL) return -1;
    }
    if(p->judge)
    {
        if(p->rem) return 0;
        p->rem=true; return 1;
    }
    return -1;
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        char ss[100];
        scanf("%s",&ss);
        ins(ss);
    }
    m=read();
    for(int i=1;i<=m;i++)
    {
        char ss[100];
        scanf("%s",&ss);
        int ans=find(ss);
        if(ans==-1)cout<<"WRONG"<<endl;
        else if(ans==1)cout<<"OK"<<endl;
        else if(ans==0)cout<<"REPEAT"<<endl;
    }
    return 0;

}

数组版的

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

const int maxn=1000010;
int n,m;
int ch[maxn][26],rem[maxn],vis[maxn];
int cnt;

void ins(char *ss)
{
    int u=0;
    int len=strlen(ss);
    for(int i=0;i<len;++i)
    {
        int x=ss[i]-'a';
        if(!ch[u][x]) ch[u][x]=++cnt;
        u=ch[u][x];
    }
    rem[u]=1;
}

int find(char *ss)
{
    int len=strlen(ss);
    int u=0;
    for(int i=0;i<len;i++)
    {
        int x=ss[i]-'a';
        u=ch[u][x];
        if(!u) return -1;
    }
    if(rem[u])
    {
        if(vis[u]) return 0;
        vis[u]=1;
		return 1;
    }
    return -1;
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        char ss[100];
        scanf("%s",&ss);
        ins(ss);
    }
    m=read();
    for(int i=1;i<=m;i++)
    {
        char ss[100];
        scanf("%s",&ss);
        int ans=find(ss);
        
        if(ans==-1) cout<<"WRONG"<<endl;
        else if(ans==1) cout<<"OK"<<endl;
        else if(ans==0) cout<<"REPEAT"<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值