字典树(模板)

P8306 【模板】字典树

题目描述

给定 n 个模式串 s1,s2,,snq 次询问,每次询问给定一个文本串 ti,请回答 s1sn 中有多少个字符串 sj 满足 tisj前缀

一个字符串 ts 的前缀当且仅当从 s 的末尾删去若干个(可以为 0 个)连续的字符后与 t 相同。

输入的字符串大小敏感。例如,字符串 Fusu 和字符串 fusu 不同。

输入格式

本题单测试点内有多组测试数据

输入的第一行是一个整数,表示数据组数 T

对于每组数据,格式如下:
第一行是两个整数,分别表示模式串的个数 n 和询问的个数 q
接下来 n 行,每行一个字符串,表示一个模式串。
接下来 q 行,每行一个字符串,表示一次询问。

输出格式

按照输入的顺序依次输出各测试数据的答案。
对于每次询问,输出一行一个整数表示答案。

输入输出样例 #1

输入 #1

3
3 3
fusufusu
fusu
anguei
fusu
anguei
kkksc
5 2
fusu
Fusu
AFakeFusu
afakefusu
fusuisnotfake
Fusu
fusu
1 1
998244353
9

输出 #1

2
1
0
1
2
1

说明/提示

数据规模与约定

对于全部的测试点,保证 1T,n,q105,且输入字符串的总长度不超过 3×106。输入的字符串只含大小写字母和数字,且不含空串。

说明

std 的 IO 使用的是关闭同步后的 cin/cout,本题不卡常。

分析

本题要求找到模式串中,测试串作为模式串的子串出现了多少次
改了半天 😦 ,字典树是一个树状字符数组,可以快速查找单词和子字符串,根节点是0,不包含字符,详细可以看字典树模板讲得很清楚

代码

#include<iostream>
#include<cstring>
#include<string>
#include<unordered_set>
#include<unordered_map>
using namespace std;

const int N = 100010,M = 3e6 + 10;
int son[M][62],cnt[M],idx = 0;
char s[M];
inline int atn(char c) {               //分开映射到不同区间
  if(c >= 'a' && c <= 'z') return c - 'a';
  if(c >= 'A' && c <= 'Z') return c - 'A' + 26;
  return c - '0' + 52;
 }

void insert(char str[])      //插入字符串
{
  int p = 0,len = strlen(str);  //p就是代表树的深度,
  for(int i = 0;i < len ;++i)
    {
        int u = atn(str[i]);            
        if(!son[p][u]) son[p][u] = ++idx;   //如果在p深度下没有对应字符,那么创建新的字符分支,idx表示用到的字符个数
        p = son[p][u];                       //son[p][u]代表在p深度时候u字符存在
        cnt[p]++;                            //记录该位置字符出现次数,这里由于题目要求,不是记录的字符串末尾
    }

}

int query(char str[]) {           //查询字符串
  int p = 0,len = strlen(str);
  for(int i = 0;i < len ;++i) {    //
      int u = atn(str[i]);
      if(!son[p][u]) return 0;      //存在查询过程中的字符不存在就返回0
      p = son[p][u];                
    }
    return cnt[p];

}

int main() {

  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);
  cout.tie(nullptr);


  int T;
  cin >> T;
  while(T--) {
    // memset(son,0,sizeof(son));            //memset的时间是O(n),太大会超时,由于是全局变量,只用考虑改变过的部分
    // memset(cnt,0,sizeof(cnt));               //
    for(int i = 0;i <= idx; ++i)                //一共用了idx个位置
      for(int j = 0;j < 62; ++j)
          son[i][j] = 0;
    for(int i = 0;i <= idx; ++i) cnt[i] = 0;      //一样,
    idx = 0;                                    //使用过后清零,不然会一直叠加下去
    int n,q;
    cin >> n >> q;
    while(n--) { cin >> s; insert(s); }
    while(q--) {cin >> s;cout << query(s) << endl; }
  }
    return 0;
}
posted @   bakul  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示