Divljak
Divljak
Alice 有 个字符串 ,Bob有一个字符串集合 ,一开始集合是空的。
接下来会发生 个操作,操作有两种形式:
“ ”,Bob 往自己的集合里添加了一个字符串 。
“ ”,Alice 询问 Bob,集合 中有多少个字符串包含串 。
(我们称串 包含串 ,当且仅当 是 的子串)
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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .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语句:使用策略模式优化代码结构