CF666E-Forensic Examination【广义SAM,线段树合并】

1|0正题

题目链接:https://www.luogu.com.cn/problem/CF666E


1|1解题思路

给出一个串Sn个串Tim次询问SabTlr中出现的最多次数并且输出这个串的编号。

1|s|5×105,Ti5×104,1m5×105


1|2解题思路

ST丢一起跑一个广义SAM

两个串包含当且仅当他们在SAM上对应节点是父子,所以直接对于每个节点开一个线段树,然后T的每个位置对应编号加一。

对于询问S子串直接倍增跳到对应位置然后用线段树合并上来的东西求答案就好了。


1|3code

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e6+2e5+10,S=19; int answ,ansv; struct SegTree{ int cnt,w[N*20],v[N*20],ls[N*20],rs[N*20]; void PushUp(int x,int L,int R){ w[x]=-1; if(w[ls[x]]>w[x])w[x]=w[ls[x]],v[x]=max(v[ls[x]],L); if(w[rs[x]]>w[x])w[x]=w[rs[x]],v[x]=v[rs[x]]; return; } void Change(int &x,int L,int R,int pos){ if(!x)x=++cnt; if(L==R){w[x]++;v[x]=L;return;} int mid=(L+R)>>1; if(pos<=mid)Change(ls[x],L,mid,pos); else Change(rs[x],mid+1,R,pos); PushUp(x,L,R);return; } void Ask(int x,int L,int R,int l,int r){ if(!x){if(answ<0)answ=0,ansv=l;return;} if(L==l&&R==r){if(answ<w[x])answ=w[x],ansv=v[x];return;} int mid=(L+R)>>1; if(r<=mid)Ask(ls[x],L,mid,l,r); else if(l>mid)Ask(rs[x],mid+1,R,l,r); else Ask(ls[x],L,mid,l,mid),Ask(rs[x],mid+1,R,mid+1,r); } int Merge(int x,int y,int L,int R){ if(!x||!y)return x+y;int p=++cnt; if(L==R){w[p]=w[x]+w[y];v[p]=L;return p;} int mid=(L+R)>>1; ls[p]=Merge(ls[x],ls[y],L,mid); rs[p]=Merge(rs[x],rs[y],mid+1,R); PushUp(p,L,R);return p; } }T; struct node{ int to,next; }a[N]; int n,m,l,tot,f[N][S+1],rt[N],ls[N]; int cnt,len[N],ch[N][26],fa[N],pos[N]; char s[N]; void addl(int x,int y){ a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot;return; } int Insert(int p,int c){ if(ch[p][c]){ int q=ch[p][c]; if(len[p]+1==len[q])return q; else{ int nq=++cnt;len[nq]=len[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[nq])); fa[nq]=fa[q];fa[q]=nq; for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq; return nq; } } int np=++cnt; len[np]=len[p]+1; for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np; if(!p)fa[np]=1; else{ int q=ch[p][c]; if(len[p]+1==len[q])fa[np]=q; else{ int nq=++cnt;len[nq]=len[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[nq])); fa[nq]=fa[q];fa[q]=fa[np]=nq; for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq; } } return np; } void dfs(int x){ for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; f[y][0]=x;dfs(y); rt[x]=T.Merge(rt[x],rt[y],1,n); } return; } int GetPos(int l,int r){ int x=pos[r]; for(int i=S;i>=0;i--) if(len[f[x][i]]>=r-l+1)x=f[x][i]; return x; } int main() { scanf("%s",s+1); l=strlen(s+1);cnt=1; for(int i=1,x=1;i<=l;i++){ x=Insert(x,s[i]-'a'); pos[i]=x; } scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s+1); int x=1,l=strlen(s+1); for(int j=1;j<=l;j++){ x=Insert(x,s[j]-'a'); T.Change(rt[x],1,n,i); } } for(int i=2;i<=cnt;i++)addl(fa[i],i); dfs(1); for(int j=1;j<=S;j++) for(int i=1;i<=cnt;i++) f[i][j]=f[f[i][j-1]][j-1]; scanf("%d",&m); while(m--){ int a,b,l,r; scanf("%d%d%d%d",&l,&r,&a,&b); int x=GetPos(a,b);answ=-1;ansv=0; T.Ask(rt[x],1,n,l,r); printf("%d %d\n",ansv,answ); } return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15174866.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(53)  评论(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 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示