1 条题解

  • 0
    @ 2025-8-24 22:58:15

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Atserckcn
    愿你的刀仍然锋利

    搬运于2025-08-24 22:58:15,当前版本为作者最后更新于2024-05-26 20:00:09,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    P10470 前缀统计题解

    题目简述:

    一共有 NN 个字符串 S1,S2,,SNS_1,S_2,\cdots,S_N,有 MM 次询问,每次给定字符串 TT,需要求出 NN 个字符串中一共有多少个字符是 TT前缀

    数据范围:

    总长度不超过 10610^6

    思路1:

    运用 STL 函数中的 find() 函数,每次查找 NN 个字符串,并计算其 find() 函数返回值为 00,即是 TT 的前缀的情况数。

    但是分析一下时间复杂度,我们会发现,NNMM 最大都为 10510^5,则每次匹配的最坏结果就是 105×105=101010^5 \times 10^5 =10^{10},很显然会时间超限。

    那么我们需要认识一种新的数据结构——字典树!

    trie 树

    字典树(trie 树)是一种用于实现字符串快速检索的多叉树结构。

    trie 树的每个节点都拥有若干个字符指针,若在插入或检索字符串时扫描到一个字符 c,就沿着当前节点的 c 字符指针,走向该指针指向的节点。

    下图即为一个简易版字典树,存储了单词:ab、ac、ba。

    那么该如何用代码实现字典树呢?

    像以往的树形结构一样,我们可以用结构体存树:

    struct EDGE{
    	int son[26];//因为保证只有小写字母,所以分支最多有26个
    	int cnt;//统计到这个节点为止,一共有几个前缀
    }edge[MAXN];
    

    当然,因为输入的是小写字母,但是结构体里边是数字的一维数组,所以我们还要手打一个字符串转数字的函数——

    int getnumber(char ch)//蒟蒻不会用map,勿喷
    {
    	return ch-'a';//ASCII码值
    }
    

    接下来,我们需要解决两大问题:

    1、如何将一个字符串插入字典树?

    思路:由于字典树是用来存字符的,所以我们可以将整个字符串转为一个个字符,再将其插入字典树,具体操作注释在代码中。

    void insert(string s)//插入字符串s
    {
    	now=1;//相当于一个起点
    	for(int i=0;i<s.size();i++)//分解每个字符
    	{
    		num=getnumber(s[i]);//转化
    		if(!edge[now].son[num])//如果当前字典树不存在此单词
    			edge[now].son[num]=++cnt;//加边
    		now=edge[now].son[num];//沿着现在的这条边走
    		if(i==s.size()-1) edge[now].cnt++;//特判情况,若已经是字符串的最后一个字符,则代表字典树的这个节点是一个单词的末尾,统计的cnt需要+1
    	}
    	return;
    }
    

    2、如何统计一个字符串前缀的个数?

    思路:顺着查找字符串的每个字符,一路顺着字典树,直到查完,一路上统计 cnt 个数。

    Code:

    void find(string s)//统计字符串s的前缀数量
    {
    	ans=0;//统计的变量
    	now=1;//起点
    	for(int i=0;i<s.size();i++)
    	{
    		num=getnumber(s[i]);
    		if(!edge[now].son[num])//如果到头了
    		{
    			printf("%d\n",ans);
    			return;
    		}
    		now=edge[now].son[num];//转移
    		ans+=edge[now].cnt;//统计
    	}
    	printf("%d\n",ans);
    	return;
    }
    

    完整代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1e6+5;
    int now,cnt=1,num,ans;
    int getnumber(char ch)
    {
    	return ch-'a';
    }
    struct EDGE{
    	int son[26];
    	int cnt;
    }edge[MAXN];
    int n,m;
    string s;
    void insert(string s)
    {
    	now=1;
    	for(int i=0;i<s.size();i++)
    	{
    		num=getnumber(s[i]);
    		if(!edge[now].son[num])
    			edge[now].son[num]=++cnt;
    		now=edge[now].son[num];
    		if(i==s.size()-1) edge[now].cnt++;
    	}
    	return;
    }
    void find(string s)
    {
    	ans=0;
    	now=1;
    	for(int i=0;i<s.size();i++)
    	{
    		num=getnumber(s[i]);
    		if(!edge[now].son[num])
    		{
    			printf("%d\n",ans);
    			return;
    		}
    		now=edge[now].son[num];
    		ans+=edge[now].cnt;
    	}
    	printf("%d\n",ans);
    	return;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	{
    		cin >> s;
    		insert(s);
    	}
    	for(int i=1;i<=m;i++)
    	{
    		cin>>s;
    		find(s);
    	}
    	return 0;
    }
    

    AC 记录

    推荐关联题目:P8306 【模板】字典树,以及我写的个人记录题解

    • 1

    信息

    ID
    10171
    时间
    1000ms
    内存
    512MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者