[BZOJ 4556][Tjoi2016&Heoi2016]字符串

[BZOJ 4556] 字符串

题意

原题面

给定一个长度为 \(n\) 的串 \(s\), \(m\) 次查询 \(s[a:b]\) 的所有子串与 \(s[c:d]\) 的LCP的最大值.

\(1\le n,m\le1\times 10^5\)

题解

据说后缀数组挺好做的?

管他呢反正垃圾rvalue只会用SAM做题(QAQ)

首先SAM比较好搞的是子串公共后缀 (right集合出发的长度一定的后缀) , 于是我们把原串 std::reverse 一下再搞.

翻转之后要做的就是查询 \(s[a:b]\) 的所有子串与 \(s[c:d]\) 的最长公共后缀的最大值.

容易小于最优解的公共后缀对应的子串是可以构造出来的, 于是答案是可以二分的. 我们二分这个答案为 mid. 则我们要做的就是查询 \(s[d-mid+1:d]\) 是否是 \(s[a:b]\) 的子串.

我们如果能够定位到 \(s[d-mid+1:d]\) 在SAM上运行后的状态, 那么显然这个状态的right集合的位置都是 \(s[d-mid+1:d]\)\(s\) 中的出现位置的右端点. 于是只要right集合中有 \([a+mid-1,b]\) 内的值则代表当前二分的答案合法.

定位某个子串在SAM上运行后的状态的一般做法是在prt树上倍增. 查询 \(s[l:r]\) 的状态时, 从代表 \(s[:r]\) 的状态(这个可以在构造时存储)出发向上跳, 找到第一个 \(len(p)\ge r-l+1\) 的状态 \(p\) 即可. (再向上跳必然会导致 \(len(p)<r-l+1\), 于是当前的 \(p\) 满足 \(len(prt(p))<r-l+1\le len(p)\), 易知 \(p\) 包含 \(s[l:r]\)).

求right集合可以用线段树合并解决. 动态开点线段树合并的复杂度是有保证的 (\(O(\log n)\)).

扫描二维码关注公众号,回复: 5168783 查看本文章

参考代码

#include <bits/stdc++.h>

const int MAXN=2e5+10;

struct Node{
    int l;
    int r;
    int sum;
    Node* lch;
    Node* rch;
    Node(int,int,int=0);
    void Insert(int);
    int Query(int,int);
};
Node* N[MAXN];

int n;
int q;
int cnt=1;
int root=1;
int last=1;
int s[MAXN];
int len[MAXN];
int end[MAXN];
char str[MAXN];
int scnt[MAXN];
int pprt[20][MAXN];
int* prt=pprt[0];
std::map<char,int> chd[MAXN];

void BucSort();
int Extend(char);
inline void Rev(int&);
Node* Merge(Node*,Node*);
bool Check(int,int,int,int,int);

int main(){
    scanf("%d%d",&n,&q);
    scanf("%s",str+1);
    std::reverse(str+1,str+1+n);
    for(int i=1;i<=n;i++){
        end[i]=Extend(str[i]);
        N[end[i]]=new Node(1,n);
        N[end[i]]->Insert(i);
    }
    BucSort();
    for(int i=cnt;i>=1;i--)
        N[prt[s[i]]]=Merge(N[prt[s[i]]],N[s[i]]);
    int lg;
    for(lg=1;(1<<lg)<cnt;lg++)
        for(int i=cnt;i>=1;i--)
            pprt[lg][i]=pprt[lg-1][pprt[lg-1][i]];
    while(q--){
        int a,b,c,d;
        scanf("%d%d%d%d",&a,&b,&c,&d);
        Rev(a),Rev(b),Rev(c),Rev(d);
        std::swap(a,b),std::swap(c,d);
        int l=0,r=std::min(d-c+1,b-a+1)+1;
        while(r-l>1){
            int mid=(l+r)>>1;
            int p=end[d];
            for(int i=lg;i>=0;i--){
                if(len[pprt[i][p]]>=mid)
                    p=pprt[i][p];
            }
            if(N[p]->Query(a+mid-1,b))
                l=mid;
            else
                r=mid;
        }
        printf("%d\n",l);
    }
    return 0;
}

void BucSort(){
    memset(scnt,0,sizeof(scnt));
    for(int i=1;i<=cnt;i++)
        ++scnt[len[i]];
    for(int i=1;i<=cnt;i++)
        scnt[i]+=scnt[i-1];
    for(int i=cnt;i>=1;i--)
        s[scnt[len[i]]--]=i;
}

int Extend(char x){
    int p=last;
    int np=++cnt;
    last=np;
    len[np]=len[p]+1;
    while(p&&!chd[p].count(x))
        chd[p][x]=np,p=prt[p];
    if(!p)
        prt[np]=root;
    else{
        int q=chd[p][x];
        if(len[q]==len[p]+1)
            prt[np]=q;
        else{
            int nq=++cnt;
            len[nq]=len[p]+1;
            chd[nq]=chd[q];
            prt[nq]=prt[q];
            prt[q]=nq;
            prt[np]=nq;
            while(p&&chd[p][x]==q)
                chd[p][x]=nq,p=prt[p];
        }
    }
    return np;
}

void Node::Insert(int x){
    ++this->sum;
    if(l!=r){
        int mid=(l+r)>>1;
        if(x<=mid){
            if(this->lch==NULL)
                this->lch=new Node(l,mid);
            this->lch->Insert(x);
        }
        else{
            if(this->rch==NULL)
                this->rch=new Node(mid+1,r);
            this->rch->Insert(x);
        }
    }
}

Node* Merge(Node* a,Node* b){
    if(a==NULL)
        return b;
    if(b==NULL)
        return a;
    Node* tmp=new Node(a->l,a->r,a->sum+b->sum);
    tmp->lch=Merge(a->lch,b->lch);
    tmp->rch=Merge(a->rch,b->rch);
    return tmp;
}

Node::Node(int l,int r,int sum):l(l),r(r),sum(sum),lch(NULL),rch(NULL){}

inline void Rev(int& x){
    x=n-x+1;
}

int Node::Query(int l,int r){
    if(l<=this->l&&this->r<=r)
        return this->sum;
    else{
        int ans=0;
        if(this->lch&&l<=this->lch->r)
            ans+=this->lch->Query(l,r);
        if(this->rch&&this->rch->l<=r)
            ans+=this->rch->Query(l,r);
        return ans;
    }
}

猜你喜欢

转载自www.cnblogs.com/rvalue/p/10380954.html
今日推荐