首页 > 其他分享 >奇怪的数据结构题(Trie树合并)

奇怪的数据结构题(Trie树合并)

时间:2022-11-11 11:33:12浏览次数:44  
标签:le 数字串 idx Trie 合并 int 操作 数据结构

奇怪而又不算难的数据结构题

题面:

题目描述

有一个集合 \(a\),初始为空。

你需要写一个数据结构,支持:

  • 0 x 表示将 \(x\) 加入该集合,其中 \(x\)为一数字串。保证 不在集合中。
  • 1 x 表示查询 \(x\)是否存在于该集合中。
  • 2 x y 表示令数字串 \(x\)和 \(y\)纠缠。不保证 \(x\)和 \(y\)在集合中

另外,如果某数字串形如 \(x+z\)(\(+\) 为拼接操作),并且存在一数字串 \(x\)与\(y\) 纠缠,并且 \(y+z\)在集合中,则认为 \(x+z\)也在集合中。

注意集合中可能有无穷多个数字串。

输入格式

第一行一个正整数 \(m\),表示操作数。

接下来 \(m\)行,每行一个操作,具体见题目描述。

输出格式

对于每个 \(1\)操作,如果是输出 \(1\),否则输出 \(0\)。

样例输入

0 123 
1 123 
1 0 
2 12 13 
0 124 
1 133 
1 134 
1 13 
2 1 11 
1 111 
1 11111111111111111111111124

样例输出

1 
0 
1 
1 
0 
0 
1 

数据范围

\[令c为非询问操作次数之和,S为所有询问的数字串的总长度,L为所有非询问数字串的最长长度 \]

\[1\le m\le 1e5,0\le c\le 2050,1\le S\le 8e6,1\le L\le 50 \]

模拟赛中の奇怪遭遇吐槽

11/10的模拟赛十分傻逼诡异。T1 T3 T4全是数据结构阴间题,足以看出出题人多么傻逼牛逼。T1其实可以用set简单维护,但是我一时傻逼,觉得set肯定会寄(可能是我对stl不是那么敢用qwq)。然后就敲了250多行的线段树调了三个半小时。。。然后最后几分钟敲了T2的50分,导致我这次模拟赛考了两位数。。。

其实这题在并不难,我甚至感觉它比T1T2都简单。早知道就做T3了qwq

题解

对于插入和查询字符串是否存在的问题,不难想到是用Tire树。

显然的对于01操作可以直接用Tire来解决。

对于操作2这种纠缠操作,如果有写过后缀自动机线段树合并的lao能轻松发现,可以利用类似后缀自动机的思路。让两个字符串纠缠就类似于后缀自动机fail指针的操作,具体操作如下。

我们知道对于Tire树上的每个点都代表一个字符串。

如下图。如果将12(蓝点)20(红点)这两个串纠缠,就相当于把红点和蓝点并查集连边(绿色边,图中是将20合并入12)。

然后将这两颗子树进行类似线段树合并的操作(紫色边),相当于让并查集中所有点都可以连到并查集中其他点的儿子,注意合并过程中合并信息的同时还要进行并查集连边的操作以便于以后的合并。

个人认为这道题没有太大难点,就是合并操作有些特判需要注意,如下。

merge

合并操作代码:


void tir_merg(int &idx,int &idy){
    idx=find(idx);
    int fay=find(idy);
    if(idx==fay) return;//如果两个串已经在一个并查集里,不需要合并直接返回

    //合并过程中的操作,比较易懂
    if(!idx){
        idx=fay;
        return;
    }
    else if(!idy){
        idy=idx;
        return;
    }
    fa[fay]=idx;
    if(flt[fay]) flt[idx]=true;
    for(int i=0;i<=9;++i){
        tir_merg(tir[idx][i],tir[fay][i]);
        idx=find(idx);
        //注意如果出现类似样例中最后一组2的操作,可能会更新idx的并查集,所以每次需要更新当前的并查集。
    }
}

完整代码

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
void read(int &ret){
    int res=0,fs=1;
    char a=getchar();
    while(a<'0'||a>'9'){
        if(a=='-') fs=-1;
        a=getchar();
    }
    while(a>='0'&&a<='9'){
        res=(res<<3)+(res<<1)+(a^48);
    }
}
const int N=8e6+7;
int m;
int tir[N][10],rt=1,cnt=1;
char sx[N],sy[N];
bool flt[N];
int fa[N];
void init(){
    for(int i=1;i<=N-6;++i){
        fa[i]=i;
    }
}
int find(int x){
    return fa[x]==x ? x:fa[x]=find(fa[x]);
}
void insert(){
    int len=strlen(sx);
    int rtt=rt;
    for(int i=0;i<len;++i){
        int xt=sx[i]-'0';
        if(!tir[rtt][xt]) tir[rtt][xt]=++cnt;
        // cout<<rtt<<" "<<xt<<endl;
        rtt=find(tir[rtt][xt]);
    }
    flt[rtt]=true;
}
void ask(){
    int len=strlen(sx);
    int rtt=rt;
    for(int i=0;i<len;++i){
        int xt=sx[i]-'0';
        if(!tir[rtt][xt]){
            puts("0");
            return;
        }
        // cout<<rtt<<":"<<xt<<endl;
        rtt=tir[rtt][xt];
        rtt=find(rtt);
    }
    if(flt[rtt]) puts("1");
    else puts("0");
}
int lenx,leny;
pii tir_get(){
    pii res;
    int rtt=rt;
    for(int i=0;i<lenx;++i){
        int xt=sx[i]-'0';
        if(!tir[rtt][xt]) tir[rtt][xt]=++cnt;
        rtt=find(tir[rtt][xt]);
    }
    res.first=rtt;
    rtt=rt;
    for(int i=0;i<leny;++i){
        int xt=sy[i]-'0';
        if(!tir[rtt][xt]) tir[rtt][xt]=++cnt;
        rtt=find(tir[rtt][xt]);
    }
    res.second=rtt;
    return res;
}
void tir_merg(int &idx,int &idy){
    idx=find(idx);
    int fay=find(idy);
    if(idx==fay) return;
    if(!idx){
        idx=fay;
        return;
    }
    else if(!idy){
        idy=idx;
        return;
    }
    fa[fay]=idx;
    if(flt[fay]) flt[idx]=true;
    for(int i=0;i<=9;++i){
        tir_merg(tir[idx][i],tir[fay][i]);
        idx=find(idx);
    }
}
signed main(){
    // freopen("tarjan.in","r",stdin);
    // freopen("tarjan.out","w",stdout);
    init();
    clock_t S,E;
    S=clock();
    scanf("%d",&m);
    while(m--){
        int opt;
        scanf("%d",&opt);
        scanf("%s",sx);
        if(opt==0){
            insert();
        }else if(opt==1){
            ask();
        }else{
            scanf("%s",sy);
            lenx=strlen(sx);leny=strlen(sy);
            pii pat=tir_get();
            tir_merg(pat.first,pat.second);
        }
    }
    E=clock();
    // cout<<"yzh's run:"<<E-S<<"ms"<<endl;
    return 0;
}

标签:le,数字串,idx,Trie,合并,int,操作,数据结构
From: https://www.cnblogs.com/I-am-yzh/p/16880026.html

相关文章

  • MapReduce实战之 MapReduce中多表合并案例
     MapReduce中多表合并案例1)需求:订单数据表t_order:idpidamount1001011100202210030331001   01   11002   02   21003   03   31004   01 ......
  • 数据结构篇——链表
    数据结构篇——链表本次我们介绍基础算法中的区间合并,我们会从下面几个角度来介绍:单链表双链表单链表我们会在这里介绍单链表单链表简介我们首先来简单介绍一下单......
  • 208.实现Trie(前缀树)
    Trie(发音类似"try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。请你实现Tri......
  • 每日一题-区间合并
    MergingIntervalssort(a.begin(),a.end());vector<pair<int,int>>res;for(inti=0;i<n;++i){intl=0,r=res.size()-1;while(l<r......
  • 解决合并分支代码冲突
    1,当执行gitmerge合并代码报冲突的时候首先看到是哪个文件,在vscode里面打开,留下想要的那行代码2,修改完之后在当前的分支上进行add,commit,push这些操作3,gitmerge需要合......
  • 【数据结构-树】树及森林的定义
    目录1双亲表示法2孩子表示法2.1孩子表示法2.2双亲孩子表示法3孩子兄弟表示法1双亲表示法dataparent存储某个结点的数据信息存储该结点的双亲所在数组中......
  • Redis数据结构简介-Set
     Set结构存储值与结构读写能力:包含字符串的无序收集器(unorderedcollection),且数据不重复.添加,获取,移除单个元素;检查一个元素是否存在于集合中;......
  • Redis数据结构简介-Hash
     Hash结构存储值与结构读写能力:包含键值对的无序散列表添加,获取,移除单个键值对;获取所有键值对.存储类似HashMap的数据 hash是日常开发过......
  • Redis数据结构简介-List
     List结构存储值与结构读写能力:一个链表,链表上的每个节点都包含了一个字符串从链表的两端推入或者弹出元素;根据偏移量对链表进行修剪(trim);读取单......
  • 前端图片合并
    上一篇中说到了电子签名,需求是用户签完名需要把名字放在某一个需要签名的位置,这里采用canvas进行图片的合并操作:话不多说,直接上代码<template><viewclass="ca......