洛谷 P8306 模板字典树

Posted zzc大魔王

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 P8306 模板字典树相关的知识,希望对你有一定的参考价值。

 


字典树 (Trie) - OI Wiki

【数据结构】字典树TrieTree图文详解_Avalon Demerzel的博客-CSDN博客

建议先看一看上面那两个网站,不透彻的可以再看我的理解一下。

由于字典树的每一个节点都是有用的,都有可能是一个单词的结尾,所以需要给每个节点打上编号,所以字典树所占的内存会非常大。

第一维大小可以直接用字符串最长长度来测试(不够再扩大)

最重要的是二维数组的含义

根据打上编号的字典树结构我们可以发现,每一个节点下面都可以跟26种情况,为了方便迭代查阅,定义二维数组为 t[当前节点编号][连接的字母]=子节点编号 

即有 t[子节点编号][连接的字母]=子节点编号的编号,直接套用自己即可。

如果对应有编号,那就说明连上了,没有编号,那就是没连着。


这道题有大写字母,小写字母,数字,一共26+26+10=62种。

将其映射到二维数组中。

int getNum(char c)
    if(c>='a' and c<='z')return c-'a';
    else if(c>='A' and c<='Z')return c-'A'+26;
    else return c-'0'+52;

根据二维数组含义实现插入。

inline void insert()
    int now=0;//从根节点开始往下连接
    for(int i=0;i<s.length();++i)
        int k= getNum(s[i]);//得到映射
        if(!t[now][k])t[now][k]=++idx;//没有这个字母的话,新建一个节点
        now=t[now][k];//往下搜
        cnt[now]++;//记录所有前缀,此题要用
    

和插入基本一样的查找。

int find()
    int now=0;
    for(int i=0;i<s.length();++i)
        int k= getNum(s[i]);
        if(!t[now][k])return 0;
        now=t[now][k];
    
    return cnt[now];

最后,由于初始化一直在用memset一直导致TLE。

原因应该是需要被初始化的空间(编号范围)远小于N,如果用memset去初始化所有空间就会超时。

所以这道题的初始化应该用for来手动赋值(for的手动赋值和memset在汇编上实现一致,但是for的范围更小,所以此处速度比memset快的多)

参考:memset()比C中的循环效率更高吗? |

AC代码

#include <bits/stdc++.h>
using namespace std;
const int N=3e6+10;

inline void betterCinCout()
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

int getNum(char c)
    if(c>='a' and c<='z')return c-'a';
    else if(c>='A' and c<='Z')return c-'A'+26;
    else return c-'0'+52;

int T,n,q,t[N][63],idx,cnt[N];
string s;
inline void insert()
    int now=0;//从根节点开始往下连接
    for(int i=0;i<s.length();++i)
        int k= getNum(s[i]);//得到映射
        if(!t[now][k])t[now][k]=++idx;//没有这个字母的话,新建一个节点
        now=t[now][k];//往下搜
        cnt[now]++;//记录所有前缀,此题要用
    

inline void init()
    for(int i=0;i<=idx;++i)
        for(int j=0;j<=62;++j)
            t[i][j]=0;
    for(int i=0;i<=idx;++i)
        cnt[i]=0;
    idx=0;

int find()
    int now=0;
    for(int i=0;i<s.length();++i)
        int k= getNum(s[i]);
        if(!t[now][k])return 0;
        now=t[now][k];
    
    return cnt[now];


int main()
    betterCinCout();

    cin>>T;
    while(T--)
        init();

        cin>>n>>q;
        for(int i=1;i<=n;++i)
            cin>>s;
            insert();
        

        for(int i=1;i<=q;++i)
            cin>>s;
            cout<<find()<<endl;
        
    
    return 0;

洛谷P3373 模板线段树 2

 P3373 【模板】线段树 2

  •  
  • 47通过
  • 186提交
  • 题目提供者HansBug
  • 标签
  • 难度提高+/省选-

 提交  讨论  题解  

最新讨论

  • 为啥WA(TAT)

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.将某区间每一个数乘上x

3.求出某区间每一个数的和

输入输出格式

输入格式:

第一行包含三个整数N、M、P,分别表示该数列数字的个数、操作的总个数和模数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k

操作2: 格式:2 x y k 含义:将区间[x,y]内每个数加上k

操作3: 格式:3 x y 含义:输出区间[x,y]内每个数的和对P取模所得的结果

输出格式:

输出包含若干行整数,即为所有操作3的结果。

输入输出样例

输入样例#1

5 5 38

1 5 4 2 3

2 1 4 1

3 2 5

1 2 4 2

2 3 5 5

3 1 4

输出样例#1

17

2

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

(数据已经过加强^_^)

样例说明:

                       

故输出应为17、2(40 mod 38=2)

分析:参见:传送门,一模一样.

以上是关于洛谷 P8306 模板字典树的主要内容,如果未能解决你的问题,请参考以下文章

字典树模板

统计难题HDU - 1251map打表或字典树字典树模板

字典树模板

字典树(trie)

trie字典树模板题

AC自动机