Divljak

Divljak

Alice 有 n 个字符串 S1,S2,,Sn ,Bob有一个字符串集合 T ,一开始集合是空的。

接下来会发生 q 个操作,操作有两种形式:

1 P ”,Bob 往自己的集合里添加了一个字符串 P

2 x ”,Alice 询问 Bob,集合 T 中有多少个字符串包含串 Sx

(我们称串 A 包含串 B ,当且仅当 BA 的子串)

Bob 遇到了困难,需要你的帮助。


Sol

考虑S集建AC自动机。

每次把新加入的串扔进去匹配,所有匹配位置加1。

查询时就是子树和。

但是有重复算的情况。我们把所有要加的点按dfs序排序,然后把相邻两两的lca-1

这样就不重了,lca之上的点会+2-1

卡常差评

复制代码
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 2000006
using namespace std;
int num,n,tr[maxn][26],fail[maxn],p[maxn],tot,cnt;
int head[maxn],f[maxn],deep[maxn],dfn[maxn],sc;
int son[maxn],sz[maxn],top[maxn],tree[maxn],a[maxn];
struct node{
    int v,nex;
}e[maxn*2];
char s[maxn];
void ins(int id){
    int len=strlen(s),k=0;
    for(int i=0;i<len;i++){
        if(!tr[k][s[i]-'a'])tr[k][s[i]-'a']=++n;
        k=tr[k][s[i]-'a'];
    }
    p[id]=k;
}
void build(){
    queue<int>q;
    for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]);
    while(!q.empty()){
        int k=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[k][i]){
                fail[tr[k][i]]=tr[fail[k]][i];
                q.push(tr[k][i]);
            }
            else tr[k][i]=tr[fail[k]][i];
        }
    }
}
void lj(int t1,int t2){
    e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void dfs1(int k,int fa){
    f[k]=fa;deep[k]=deep[fa]+1;
    sz[k]=1;int gp=0;
    for(int i=head[k];i;i=e[i].nex){
        dfs1(e[i].v,k);
        sz[k]+=sz[e[i].v];
        if(sz[e[i].v]>sz[gp] || !gp)gp=e[i].v;
    }
    son[k]=gp;
}
void dfs2(int k){
    dfn[k]=++sc;
    if(son[k])top[son[k]]=top[k],dfs2(son[k]);
    for(int i=head[k];i;i=e[i].nex)
        if(e[i].v!=son[k])top[e[i].v]=e[i].v,dfs2(e[i].v);
}
void add(int i,int v){
    for(;i<=n;i+=i&-i)tree[i]+=v;
}
int ask(int i){
    int sum=0;for(;i;i-=i&-i)sum+=tree[i];
    return sum;
}
bool cmp(int a,int b){
    return dfn[a]<dfn[b];
}
int lca(int a,int b){
    int t1=top[a],t2=top[b];
    while(t1!=t2){
        if(deep[t1]<deep[t2])swap(t1,t2);
        a=f[t1],t1=top[a];
    }
    return deep[a]>deep[b]?b:a;
}
void work(){
    int tp=0;
    int k=0;int len=strlen(s);
    for(int i=0;i<len;i++){
        k=tr[k][s[i]-'a'];a[++tp]=k;
        //add(dfn[k],1);
    }
    sort(a+1,a+tp+1,cmp);
    add(dfn[a[1]],1);
    for(int i=2;i<=tp;i++)add(dfn[a[i]],1),add(dfn[lca(a[i],a[i-1])],-1);
}
int main()
{
    scanf("%d",&num);
    for(int i=1;i<=num;i++){
        scanf(" %s",s);ins(i);
    }
    build();
     
    for(int i=1;i<=n;i++)lj(fail[i],i);
    n++;dfs1(0,n);dfs2(0);
    int q;scanf("%d",&q);
    for(int i=1,op;i<=q;i++){
        scanf("%d",&op);
        if(op==1){
            scanf("%s",s);work();
        }
        else {
            int t;scanf("%d",&t);t=p[t];
            printf("%d\n",ask(dfn[t]+sz[t]-1)-ask(dfn[t]-1));
        }
    }
    return 0;
}
View Code
复制代码

 

posted @   liankewei123456  阅读(126)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示