神牛的养成计划---可持久化trie

这篇博客介绍了如何使用可持久化Trie解决在线查询字符串前缀和后缀匹配的问题。作者详细讲解了题目要求、数据范围和限制,并提供了题解思路和代码实现。通过建立字典树并利用可持久化Trie,可以高效地找出满足特定条件的字符串区间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定n个由小写字母组成的字符串。现在有m个询问,每个询问指定两个字符串s1,s2.要求回答:在给定的n个字符串中,有多少个字符串s满足:s1是s的前缀且s2是s的后缀。强制在线!

数据范围

nL12106ms1,s2L22106,n2000m100000.

限制

时间限制:1s
空间限制:256M (512M) (原题限制256M,这个太丧心病狂了!作为蒟蒻,我只会写512M限制的!)

题解

对给定的n个字符串建一棵字典树.对于询问,我们先在trie上匹配,假设最后停在了p节点,那么所有满足条件的字符串s的末尾节点一定是在p的子树中的。如果我们把原字符串按照字典序排序(其实就是trie的dfs序),那么满足条件的s所对应的编号一定落在某个连续区间内。于是我们用可持久化trie维护后缀就好了。

第一次写可持久化trie,感觉还算比较好写。

看别人的题解,都是自定义cmp函数,然后sort一下。只有我蠢蠢地按dfs序排序。

代码

#include<cstdio> 
#include<cstring> 
#include<iostream> 
#include<algorithm> 
#include<vector> 
using namespace std; 
#define MAXN 2000100 
char s[MAXN],t[MAXN]; 
int _l[MAXN],_r[MAXN]; 
int n,m,tot=1; 
int to[MAXN][26],rt[MAXN],nxt[MAXN][26],summ[MAXN],mn[MAXN],mx[MAXN],order[MAXN]; 
int C[MAXN]; 
vector<int>P[MAXN]; 
void ins(int id) 
{ 
    int x,p=1; 
    for(int i=0;t[i]!='\0';i++) 
    { 
        x=t[i]-'a'; 
        if(!to[p][x]) 
        { 
            to[p][x]=++tot; 
        } 
        p=to[p][x]; 
    } 
    P[p].push_back(id); 
    return ; 
} 
int tim=0; 
const int inf=0x3c3c3c3c; 
void dfs(int p) 
{ 
    mn[p]=inf; 
    mx[p]=0; 
    for(int i=0;i<P[p].size();i++) 
    { 
        order[P[p][i]]=++tim; 
        mn[p]=min(mn[p],tim); 
        mx[p]=max(mx[p],tim); 
    } 
    for(int i=0;i<26;i++) 
    { 
        if(to[p][i]) 
        { 
            dfs(to[p][i]); 
            mn[p]=min(mn[p],mn[to[p][i]]); 
            mx[p]=max(mx[p],mx[to[p][i]]); 
        } 
    } 
    return ; 
} 
bool cmp(int a,int b) 
{ 
    return order[a]<order[b]; 
} 
void ins2(int& p,int f,int d,int pp) 
{ 
    p=++tot; 
    summ[p]=summ[f]+1; 
    memcpy(nxt[p],nxt[f],sizeof(nxt[f])); 
    if(d>pp) 
    { 
        ins2(nxt[p][s[d]-'a'],nxt[f][s[d]-'a'],d-1,pp); 
    } 
    return ; 
} 
int lstans=0; 
int main() 
{ 
    scanf("%d",&n); 
    for(int i=1,tmp;i<=n;i++) 
    { 
        scanf("%s",t); 
        _l[i]=_r[i-1]+1; 
        tmp=strlen(t); 
        _r[i]=_l[i]+tmp-1; 
        for(int j=0;j<tmp;j++) 
        { 
            s[_l[i]+j]=t[j]; 
        } 
        C[i]=i; 
        ins(i); 
    } 
    dfs(1); 
    sort(C+1,C+1+n,cmp); 
    tot=1; 
    for(int i=1,I;i<=n;i++) 
    { 
        I=C[i]; 
        ins2(rt[i],rt[i-1],_r[I],_l[I]); 
    } 
    int L,R; 
    scanf("%d",&m); 
    for(int i=1;i<=m;i++) 
    { 
        scanf("%s",t); 
        L=inf; 
        R=0; 
        int x=1; 
        for(int c,j=0;t[j]!='\0';j++) 
        { 
            c=t[j]-'a'; 
            c=c+lstans; 
            c%=26;//这个傻逼错误,我调了半天才发现!!c-=c>=26?26:0; 
            x=to[x][c]; 
        } 
        if(x) 
        { 
            L=mn[x]; 
            R=mx[x]; 
        } 
        scanf("%s",t); 
        if(L>R) 
        { 
            lstans=0; 
        } 
        else 
        { 
            int x=rt[L-1],y=rt[R],c,nn=strlen(t); 
            for(int j=nn-1;j>=0;j--) 
            { 
                c=t[j]-'a'; 
                c=c+lstans; 
                c%=26;//c-=c>=26?26:0; 
                x=nxt[x][c]; 
                y=nxt[y][c]; 
            } 
            lstans=summ[y]-summ[x]; 
        } 
        printf("%d\n",lstans); 
    } 
    return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值