[LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字

[LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字

題意

給定一個大串 \(S\) 以及 \(q\) 次詢問, 每次詢問給定一個串 \(T\) 和區間 \([l,r]\), 求 \(T\) 中有多少本質不一樣的子串不是 \(S[l:r]\) 的子串.php

\(|S|\le 5\times 10^5,q\le 10^5,\sum|T|\le10^6\).c++

題解

普通的碼農字符串題...數組

得到成就: \(40\texttt{min}(2400\texttt{s})\) 內打完 \(3.9\texttt{kB}\) 的代碼(然而並無打完就A...仍是太菜了...)curl

感受考場上若是T1T2沒打滿的話寫個 \(68\) 分沙雕SAM暴力(詢問區間都是原串的 \(17\) 個測試點)就能夠跑路了...分高好寫還不用調...測試

我的的大致思路是: 由於求本質不一樣子串個數是容易的, 因此先補集轉化爲求 \(T\) 的全部本質不一樣的子串中是 \(S[l:r]\) 的串的個數.ui

按照套路咱們維護一個相似掃描線的東西, 用SAM對 \(T\) 的全部下標 \(i\) 求出以 \(i\) 爲右端點且是 \(S[l:r]\) 的子串的最長子串長度. 按照SAM的套路, 這部分的計算就是直接用 \(T\)\(S\) 的SAM上面跑, 若是能夠匹配就匹配, 不能匹配跳到後綴自動機的父親節點上來移動左端點.this

按照上面這樣計算是對整串來講的. 由於還要考慮區間 \([l,r]\) 的事情, 咱們用線段樹合併維護出每一個結點的right集合, 設當前匹配長度爲 \(len\), 那麼只有噹噹前狀態的right集合與 \([l+len-1,r]\) 有交集才說明與 \(S[l:r]\) 匹配. 若是不知足這個條件, 不能按照SAM的普通套路直接跳prt, 而是應該讓 \(len\) 減小 \(1\). 直接跳的話左端點會移動若干個位置, 可能會跳過最優長度. SAM普通套路直接跳prt是由於若是 \(len\) 只減小 \(1\) 而沒有到達prt的長度的話依然沒有改變當前狀態不能匹配的事實.url

然而直接這樣計算鐵定會有重複, 咱們對 \(T\) 的反串建SA求出全部前綴的最長公共後綴長度做爲去重的參考信息. 按照SA求本質不一樣子串個數的套路, 重複的子串必然出如今後綴數組上相鄰的兩個後綴(注意是反串的後綴)上. 假設相鄰的兩個後綴 \(i,j\) 的在原串的對應前綴的最大匹配長度分別是 \(mlen_i,mlen_j\) 且它們的LCP是 \(height\) 的話, 貢獻就是 \(mlen_j-\min(height,mlen_i)\).spa

實際上 \(mlen_j\) 確定不會小於 \(\min(height,mlen_i)\), 由於這部分串是徹底同樣的. 因此直接減就好了.指針

之前用map的寫法被UOJ 64位指針debuff給卡內存了qaq...然而指針線段樹依然在被卡內存...UOJ變97分了QAQ

參考代碼

以前據說今天minusT要更新Subterranean Rose? 完蛋在學校看不了qaq

#include <bits/stdc++.h>

const int MAXN=1e6+10;
typedef long long intEx;

struct Node{
    int l;
    int r;
    int sum;
    Node* lch;
    Node* rch;
    Node(int,int);
    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 SA[MAXN];
int len[MAXN];
int prt[MAXN];
int buc[MAXN];
int mlen[MAXN];
char buf[MAXN];
int rank[MAXN];
int height[MAXN];
int chd[MAXN][26];
int* x=new int[MAXN];
int* y=new int[MAXN];

void BuildSAM();
void Extend(char);
void BuildSA(char*,int);
Node* Merge(Node*,Node*);

int main(){
    freopen("name.in","r",stdin);
    freopen("name.out","w",stdout);
    scanf("%s",buf+1);
    n=strlen(buf+1);
    for(int i=1;i<=n;i++)
        Extend(buf[i]);
    BuildSAM();
    scanf("%d",&q);
    while(q--){
        int l,r;
        scanf("%s",buf+1);
        scanf("%d%d",&l,&r);
        int m=strlen(buf+1);
        int cur=root,curlen=0;
        for(int i=1;i<=m;i++){
            int x=buf[i]-'a';
            while(cur!=root&&!chd[cur][x]){
                cur=prt[cur];
                curlen=len[cur];
            }
            if(chd[cur][x]){
                ++curlen;
                cur=chd[cur][x];
                while(cur!=root&&!N[cur]->Query(l+curlen-1,r)){
                    --curlen;
                    if(curlen<=len[prt[cur]])
                        cur=prt[cur];
                }
            }
            mlen[i]=curlen;
        }
        std::reverse(buf+1,buf+m+1);
        BuildSA(buf,m);
        intEx ans=0;
        int last=0;
        for(int i=1;i<=m;i++){
            ans+=(m-SA[i]+1)-height[i];
            last=std::min(height[i],last);
            ans-=mlen[m-SA[i]+1]-last;
            last=mlen[m-SA[i]+1];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

void BuildSAM(){
    memset(buc,0,sizeof(int)*(n+1));
    for(int i=1;i<=cnt;i++)
        ++buc[len[i]];
    for(int i=1;i<=n;i++)
        buc[i]+=buc[i-1];
    for(int i=cnt;i>=1;i--)
        s[buc[len[i]]--]=i;
    for(int i=cnt;i>=1;i--)
        N[prt[s[i]]]=Merge(N[prt[s[i]]],N[s[i]]);
}

void Extend(char ch){
    int p=last;
    int x=ch-'a';
    int np=++cnt;
    last=np;
    len[np]=len[p]+1;
    N[np]=new Node(1,n);
    N[np]->Insert(len[np]);
    while(p&&!chd[p][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;
            memcpy(chd[nq],chd[q],sizeof(chd[q]));
            N[nq]=new Node(1,n);
            len[nq]=len[p]+1;
            prt[nq]=prt[q];
            prt[q]=nq;
            prt[np]=nq;
            while(p&&chd[p][x]==q)
                chd[p][x]=nq,p=prt[p];
        }
    }
}

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

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

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

void BuildSA(char* s,int n){
    int m=127;
    memset(buc+1,0,sizeof(int)*m);
    for(int i=1;i<=n;i++)
        ++buc[x[i]=s[i]];
    for(int i=1;i<=m;i++)
        buc[i]+=buc[i-1];
    for(int i=n;i>=1;i--)
        SA[buc[x[i]]--]=i;
    for(int k=1;k<n;k<<=1){
        int p=0;
        for(int i=n-k+1;i<=n;i++)
            y[++p]=i;
        for(int i=1;i<=n;i++)
            if(SA[i]>k)
                y[++p]=SA[i]-k;
        memset(buc+1,0,sizeof(int)*m);
        for(int i=1;i<=n;i++)
            ++buc[x[i]];
        for(int i=1;i<=m;i++)
            buc[i]+=buc[i-1];
        for(int i=n;i>=1;i--)
            SA[buc[x[y[i]]]--]=y[i];
        std::swap(x,y);
        x[SA[1]]=1;
        p=1;
        for(int i=2;i<=n;i++)
            x[SA[i]]=(y[SA[i]]==y[SA[i-1]]&&y[SA[i]+k]==y[SA[i-1]+k])?p:++p;
        if(p>=n)
            break;
        m=p;
    }
    for(int i=1;i<=n;i++)
        rank[SA[i]]=i;
    int k=0;
    for(int i=1;i<=n;i++){
        if(rank[i]==1)
            continue;
        if(k)
            --k;
        int j=SA[rank[i]-1];
        while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])
            ++k;
        height[rank[i]]=k;
    }
}

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

相關文章
相關標籤/搜索