ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【题解】[HEOI2016/TJOI2016]字符串

2021-08-22 20:32:08  阅读:142  来源: 互联网

标签:ch 前缀 int 题解 mid len pa TJOI2016 HEOI2016


[HEOI2016/TJOI2016]字符串

\(\text{Solution:}\)

记录一下这题获得的启发。

  • 最长公共前缀/后缀一类问题,具有可二分性。

考虑二分一个答案,然后看如何检验。

如果答案是 \(mid,\) 那么对应的串应该就是 \(s[c\cdots c+mid-1],\) 我们发现:它是前缀 \(s[1\cdots c+mid-1]\) 的后缀。

  • 通过前缀把子串转化成后缀

假定我们找到了一个点,它代表的串包含了 \(s[c\cdots c+mid-1],\) 如何查看是不是满足有一个 \(s[a\cdots b]\) 的子串是其前缀呢?

我们发现:它是区间内的 所有子串,也就是说,只要出现过即可

那么就自然想到用 endpos 来判断。

考虑一下,当前答案是 \(mid,\) 如果要满足,其结束位置至少应该在 \(a+mid-1.\) 也就是说,我们需要查询这个点的 endpos 是否包含了区间 \([a+mid-1,b]\) 中的一个点。

这就需要线段树维护 endpos 的 trick 了。

接下来思考如何定位节点。考虑我们的 parent 树实际上是一棵 前缀树 ,它满足:每一条叶节点到根的路径对应一个前缀,同时,每一个非叶子节点到根的路径都对应了某前缀的后缀。

也就是说,我们定位到一个点后,向上跳父亲,实际就是在不断找后缀的过程

那么我们前文所述,把它看成了前缀 \(s[1\cdots c+mid-1]\) 的后缀,于是我们思考,如何定位一个前缀?

这简单,从根开始依次匹配,匹配到的点对应的前缀就是答案。

那怎么往上跳定位呢?考虑经典技巧:倍增

考虑如何判断是不是跳到了对应节点:它一定满足,自身的 \(len\ge mid,\) 其父亲的 \(len<mid.\) 而 \(len\) 自下向上又具有单调性

考虑倍增跳父亲即可。剩下的就是线段树上查询了。复杂度 \(O(n\log^2 n).\)

#include<bits/stdc++.h>
using namespace std;
const int N=4e6+10;
const int SN=2e5+10;
int n,m;
char s[SN];
int sufpos[SN];
inline int Min(int x,int y){return x<y?x:y;}
inline int Max(int x,int y){return x>y?x:y;}
namespace SGT{
    int ls[N],rs[N],node;
    void change(int &x,const int &L,const int &R,const int &pos){
        if(!x)x=++node;
        if(L==R)return;
        int mid=(L+R)>>1;
        if(pos<=mid)change(ls[x],L,mid,pos);
        else change(rs[x],mid+1,R,pos);
    }
    int merge(const int &x,const int &y){
        if(!x||!y)return x|y;
        int p=++node;
        ls[p]=merge(ls[x],ls[y]);
        rs[p]=merge(rs[x],rs[y]);
        return p;
    }
    bool query(const int &x,const int &L,const int &R,const int &l,const int &r){
        if(!x)return 0;
        if(L>=l&&R<=r)return 1;
        int mid=(L+R)>>1;
        int res=0;
        if(l<=mid)res=query(ls[x],L,mid,l,r);
        if(mid<r)res|=query(rs[x],mid+1,R,l,r);
        return res;
    }
}
using namespace SGT;
namespace SAM{
    int len[SN],ch[SN][26],pa[SN],rt[SN],last=1,tot=1;
    vector<int>G[N];
    int f[N][21];
    void insert(const int &c){
        int p=last;
        int np=++tot;
        last=tot;len[np]=len[p]+1;
        for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
        if(!p)pa[np]=1;
        else{
            int q=ch[p][c];
            if(len[q]==len[p]+1)pa[np]=q;
            else{
                int nq=++tot;
                len[nq]=len[p]+1;
                memcpy(ch[nq],ch[q],sizeof ch[q]);
                pa[nq]=pa[q];pa[q]=pa[np]=nq;
                for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
            }
        }
    }
    void dfs(int x){
        for(auto v:G[x]){
            f[v][0]=x;
            for(int i=1;i<21;++i)f[v][i]=f[f[v][i-1]][i-1];
            dfs(v);
            rt[x]=merge(rt[x],rt[v]);
        }
    }
    void Build(){
        for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
        dfs(1);
    }
}
using namespace SAM;
bool check(int mid,int a,int b,int c){
    //endpos in [a+mid-1,b]
    //Let us find the position of c.
    int posc=sufpos[c+mid-1];
    //the sequence is [c,c+mid-1]
    for(int i=20;~i;--i)if(len[f[posc][i]]>=mid)posc=f[posc][i];
    return query(rt[posc],1,n,a+mid-1,b);
}
int main(){
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    for(int i=1;i<=n;++i)insert(s[i]-'a');
    for(int i=1,now=1;i<=n;++i){
        int v=s[i]-'a';
        now=ch[now][v];
        sufpos[i]=now;
        change(rt[now],1,n,i);
    }
    Build();
    while(m--){
        int a,b,c,d;
        scanf("%d%d%d%d",&a,&b,&c,&d);
        int len1=b-a+1;
        int len2=d-c+1;
        int l=1,r=Min(len1,len2);
        int Ans=-1;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid,a,b,c))l=mid+1,Ans=mid;
            else r=mid-1;
        }
        printf("%d\n",Ans);
    }
    return 0;
}

标签:ch,前缀,int,题解,mid,len,pa,TJOI2016,HEOI2016
来源: https://www.cnblogs.com/h-lka/p/15173371.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有