P4094 [HEOI2016/TJOI2016] 字符串

P4094 [HEOI2016/TJOI2016] 字符串

[HEOI2016/TJOI2016] 字符串

题目描述

佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为 n 的字符串 s,和 m 个问题。佳媛姐姐必须正确回答这 m 个问题,才能打开箱子拿到礼物,升职加薪,出任 CEO,嫁给高富帅,走上人生巅峰。

每个问题均有 a,b,c,d 四个参数,问你子串 s[a..b] 的所有子串和 s[c..d] 的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

对于 100% 的数据,1n,m100,000,字符串中仅有小写英文字母,abcd1a,b,c,dn

Solution:

首先我们注意读题:我们要求s[a..b]所有子串s[c..d]最长公共前缀。换句话说,答案的左端点其实已经固定了就是 c,我们要求的是最大化一个 c,使得 [c,ans][a,b] 上能找到匹配 。

我们考虑二分答案,我们在建 SAM 时记录每个点所对应的节点 pos[i]。这样一来,我们在 parent 树上从 pos[c+ans1] 开始向上跳,跳到最后一个满足 len[now]>ansnow.那么就说明 [c,c+ans-1] 这个字符串是被包含在这个 endpos 等价类 中的。而我们判断该答案是否可行的方法也很简单,只要 [a+ans1,b] 中任意一个点在 endpos 出现过就好了。

维护 endpos 的方法也不难想到: 线段树合并

Code:

#include<bits/stdc++.h>
const int N=4e5+5;
const int lg=19;
using namespace std;
int n,m;
struct Segmnet_Tree{
int rt[N],cnt;
struct Tree{
int ls,rs,cnt;
}t[N*32];
void pushup(int x){t[x].cnt=t[t[x].ls].cnt+t[t[x].rs].cnt;}
void insert(int &x,int l,int r,int pos)
{
t[x= (x ? x : ++cnt)].cnt++;
if(l==r)return;int mid=l+r>>1;
if(pos<=mid)insert(t[x].ls,l,mid,pos);
if(mid<pos)insert(t[x].rs,mid+1,r,pos);
}
int merge(int x,int y,int l,int r)
{
if(!x||!y)return x|y;
int res=++cnt;t[res].cnt=t[x].cnt+t[y].cnt;
if(l==r)return res;int mid=l+r>>1;
t[res].ls=merge(t[x].ls,t[y].ls,l,mid);
t[res].rs=merge(t[x].rs,t[y].rs,mid+1,r);
return res;
}
int query(int x,int l,int r,int L,int R)
{
if(!x||R<l||r<L)return 0;
if(L<=l&&r<=R)return t[x].cnt;
int mid=l+r>>1,res=0;
res+=query(t[x].ls,l,mid,L,R);
res+=query(t[x].rs,mid+1,r,L,R);
return res;
}
}T;
struct SAM{
int ch[N][26],len[N],f[N][lg+5];
int cnt,last;
void init(){cnt=last=1;}
struct Edge{
int to,nxt;
}e[N];
int head[N];
void add(int x,int y){e[++head[0]]={y,head[x]};head[x]=head[0];}
void insert(int c)
{
int p=last,q=++cnt;len[q]=len[last]+1;last=q;
for(;p&&!ch[p][c];p=f[p][0])ch[p][c]=q;
if(!p){f[q][0]=1;return;}int x=ch[p][c];
if(len[x]==len[p]+1){f[q][0]=x;return;}
int y=++cnt;len[y]=len[p]+1;f[y][0]=f[x][0];
for(int i=0;i<26;i++)ch[y][i]=ch[x][i];
f[x][0]=f[q][0]=y;
for(;p&&ch[p][c]==x;p=f[p][0])ch[p][c]=y;
}
void build()
{
for(int u=2;u<=cnt;u++){add(f[u][0],u);}for(int j=1;j<=lg;j++)for(int u=1;u<=cnt;u++)f[u][j]=f[f[u][j-1]][j-1];
}
void dfs(int x)
{
for(int i=head[x],y=e[i].to;i;i=e[i].nxt,y=e[i].to)
{
dfs(y);T.rt[x]=T.merge(T.rt[x],T.rt[y],1,n);
//cout<<"merge:"<<x<<" "<<y<<"\n";
}
}
}sam;
char s[N];
int id[N];
bool check(int a,int b,int c,int d,int mid)
{
int now=id[c+mid-1];
for(int j=lg;j>=0;j--){if(sam.f[now][j]&&sam.len[sam.f[now][j]]>=mid){now=sam.f[now][j];}}
int tmp=T.query(T.rt[now],1,n,a+mid-1,b);
//cout<<"query:"<<id[c+mid-1]<<" "<<now<<": ["<<a+mid-1<<" "<<b<<"]"<<tmp<<"\n";
return tmp>0;
}
void work()
{
cin>>n>>m;
scanf("%s",s+1);
sam.init();
id[0]=1;
for(int i=1;i<=n;i++)
{
sam.insert(s[i]-'a');id[i]=sam.last;
T.insert(T.rt[id[i]],1,n,i);
}
sam.build();sam.dfs(1);
for(int i=1,a,b,c,d;i<=m;i++)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
int l=0,r=min(b-a+1,d-c+1),mid=0,ans=0;
while(l<=r)
{
mid=l+r>>1;
if(check(a,b,c,d,mid))ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
}
int main()
{
//freopen("str.in","r",stdin);freopen("str.out","w",stdout);
work();
return 0;
}
posted @   liuboom  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示