P4094 [HEOI2016/TJOI2016]字符串

题意

考虑二分答案\(mid\),现在我们要判断\(s[c...c+mid-1]\)是否在\(s[a...b]\)出现过。

首先找到\(s[c...c+mid-1]\)所在的状态:
建出\(parent\ tree\),从\(s[1...c+mid-1]\)的节点(这个可以记录)用倍增向上跳到最后一个\(len\geqslant mid\)的节点即可,记这个节点为\(now\)。

之后我们要判断\(now\)的\(endpos\)中是否含有\([a+mid-1,b]\)中的某个数,我们给每个节点开个权值线段树用来维护该节点\(endpos\)(相当于桶),从\(parent\ tree\)向上合并线段树即可。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=500010;
int n,m,tot,cnt,t;
int id[maxn],root[maxn],head[maxn];
int f[maxn][20];
char s[maxn];
struct edge{int to,nxt;}e[maxn<<1];
struct Seg
{
    #define lc(p) (seg[p].lc)
    #define rc(p) (seg[p].rc)
    #define sum(p) (seg[p].sum)
    int lc,rc,sum;
}seg[maxn*60];
inline int read()
{
    char c=getchar();int res=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
    return res*f;
}
inline void add_edge(int u,int v)
{
    e[++cnt].nxt=head[u];
    head[u]=cnt;
    e[cnt].to=v;
}
inline void up(int p){sum(p)=sum(lc(p))+sum(rc(p));}
void insert(int &p,int l,int r,int pos)
{
    if(!p)p=++tot;
    sum(p)++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pos<=mid)insert(lc(p),l,mid,pos);
    else insert(rc(p),mid+1,r,pos);
}
int query(int p,int l,int r,int ql,int qr)
{
    if(l>=ql&&r<=qr)return sum(p);
    int mid=(l+r)>>1,res=0;
    if(ql<=mid)res+=query(lc(p),l,mid,ql,qr);
    if(qr>mid)res+=query(rc(p),mid+1,r,ql,qr);
    return res;
}
int merge(int p,int q,int l,int r)
{
    if(!p||!q)return p+q;
    int x=++tot,mid=(l+r)>>1;sum(x)=sum(p)+sum(q);
    if(l==r)return x;
    lc(x)=merge(lc(p),lc(q),l,mid);
    rc(x)=merge(rc(p),rc(q),mid+1,r);
    return x;
}
struct SAM
{
    int tot,last;
    int fa[maxn],len[maxn];
    int ch[maxn][30];
    SAM(){last=tot=1;}
    inline void add(int c)
    {
        int now=++tot;len[now]=len[last]+1;
        int p=last;last=now;
        while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
        if(!p){fa[now]=1;return;}
        int q=ch[p][c];
        if(len[q]==len[p]+1)fa[now]=q;
        else
        {
            int nowq=++tot;
            len[nowq]=len[p]+1;
            memcpy(ch[nowq],ch[q],sizeof(ch[q]));
            fa[nowq]=fa[q];fa[q]=fa[now]=nowq;
            while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
        }
    }
}sam;
void dfs(int x)
{
    for(int i=1;i<=t;i++)f[x][i]=f[f[x][i-1]][i-1];
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        f[y][0]=x;dfs(y);
        root[x]=merge(root[x],root[y],1,n);
    }
}
inline bool check(int mid,int a,int b,int c,int d)
{
    int now=id[c+mid-1];
    for(int i=t;~i;i--)if(f[now][i]&&sam.len[f[now][i]]>=mid)now=f[now][i];
    return query(root[now],1,n,a+mid-1,b)>0;
}
int main()
{
    n=read(),m=read();
    scanf("%s",s+1);
    id[0]=1;
    for(int i=1;i<=n;i++)sam.add(s[i]-'a'),id[i]=sam.last,insert(root[sam.last],1,n,i);
    for(int i=2;i<=sam.tot;i++)add_edge(sam.fa[i],i);
    t=(int)log2(sam.tot)+1;dfs(1);
    while(m--)
    {
        int a=read(),b=read(),c=read(),d=read();
        int l=0,r=min(b-a+1,d-c+1),ans=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid,a,b,c,d))ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/nofind/p/12056476.html

时间: 2024-10-18 18:37:49

P4094 [HEOI2016/TJOI2016]字符串的相关文章

[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)

[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树) 题面 给出一个长度为\(n\)的字符串\(s\),以及\(m\)组询问.每个询问是一个四元组\((a,b,c,d)\),问\(s[a,b]\)的所有子串和字符串\(s[c,d]\)的最长公共前缀长度的最大值. \(n,m \leq 10^5\) 分析 显然答案有单调性.首先我们二分答案\(mid\),考虑如何判定. 如果mid这个答案可行,那么一定存在一个后缀x,它的开头在\([a,

[HEOI2016/TJOI2016]字符串

题解: 一道挺水的题目 首先暴力是nm的 后缀数组o(1)判断 然后考虑一下正解: 首先跟后缀数组有关先考虑下二分答案.. 然后再二分出rank与它相邻多少的后缀能满足条件 然后查找一下当前区间(注意右端点是n-k+1)是否存在rank在这一大小范围的数 这个主席数维护一下就可以了 原文地址:https://www.cnblogs.com/yinwuxiao/p/8850266.html

【[HEOI2016/TJOI2016]字符串】

码农题啊 上来先无脑一个\(SA\)的板子,求出\(SA\)和\(het\)数组 我们只需要从\(sa[i]\in[a,b]\)的所有\(i\)中找到一个\(i\)使得\(sa[i]\)和\(rk[c]\)之间的最小值最大就好了 但是还必须得满足\(sa[i]+lcp-1<=b\),毕竟整个串还得在\([a,b]\)内部 考虑一下二分答案 根据\(het\)数组的性质显然越靠近\(rk[c]\)的\(sa[i]\)形成的\(lcp\)越长,于是我们可以利用一个\(ST\)表加二分找到从\(rk[

HEOI2016/TJOI2016 字符串问题

题目链接:戳我 非常不好意思,因为想要排版,所以今天先只把代码贴出来,明天补题解. 40pts暴力:直接暴力匹配 #include<iostream> #include<cstring> #include<algorithm> #include<cmath> #include<cstdio> #define MAXN 100010 using namespace std; int n,m; char s[MAXN]; inline int sol

[HEOI2016/TJOI2016]排序 解题报告

[HEOI2016/TJOI2016]排序 题意 给出一个大小为 \(n\) 的排列, 对这个排列进行 \(m\) 次操作, 操作分为以下两种, 0 l r 表示将区间 \([l,r]\) 的数升序排序. 1 l r 表示将区间 \([l,r]\) 的数降序排序. 询问 \(m\) 次操作后下标为 \(q\) 的数字. 思路 不看题解打死也想不出来系列 考虑二分答案. 设当前二分的答案为 \(mid\), 把原排列中 大于等于 \(mid\) 的数标记为 \(1\), 小于 \(mid\) 的数

dtoj4542. 「TJOI / HEOI2016」字符串

4542. 「TJOI / HEOI2016」字符串 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为 $ n $ 的字符串 $ s $,和 $ m $ 个问题.佳媛姐姐必须正确回答这 $ m $ 个问题,才能打开箱子拿到礼物,升职加薪,出任 CEO,嫁给高富帅,走上人生巅峰.每个问题均有 $a, b, c, d$ 四个参数,问你子串 $s[a \ldots b]$ 的所有子串和 $s[c \ldots d]$ 的最长公共前缀的长度的最

bzoj4556【TJOI2016&amp;HEOI2016】字符串

4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 195  Solved: 103 [Submit][Status][Discuss] Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE O,嫁给高富帅,走上人生

[HEOI2016/TJOI2016]排序

4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec Memory Limit: 256 MB Submit: 2366 Solved: 1188 [Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,

[HEOI2016&amp;TJOI2016] 排序(线段树)

4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 2703  Solved: 1386[Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,