BZOJ5417: [Noi2018]你的名字

BZOJ5417: [Noi2018]你的名字

https://lydsy.com/JudgeOnline/problem.php?id=5417

分析:

后缀数组 https://www.cnblogs.com/suika/p/9426839.html

  • 现在来看写出这样的代码应该不是什么问题了,感觉比(sa)好写。
  • 先建出(T)串的后缀自动机,由于求的是本质不同子串数量,直接在(T)串的后缀自动机上统计每个结点的贡献即可。
  • 那么我们只需求出每个结点能够向左匹配的长度(h_i)
  • 答案就是(sumlimits_i max(0,len[i]-max(len[fa[i]],h[i])))
  • 转化成求(T)每个前缀和(S)串匹配的后缀长度。
  • 这个和正常匹配差不多,有(ch)就走,然后在判断是否有一个(endpos)出现在特定区间内。
  • 如果没有就减少匹配长度并向上跳。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
#define N 1000050
typedef long long ll;
char w1[N],w2[N];
int n,m,h[N];
int siz[N*20],ls[N*20],rs[N*20],tot,root[N];
int ke[N],ro[N],L,R;
char buf[100000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {
    int x=0; char s=nc();
    while(s<'0'||s>'9') s=nc();
    while(s>='0'&&s<='9') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    return x;
}
char pbuf[100000],*pp=pbuf;
void push(const char c) {
    if(pp-pbuf==100000) fwrite(pbuf,1,100000,stdout),pp=pbuf;
    *pp++=c;
}
void write(ll x) {
    static int sta[30];
    int top=0;
    do{sta[top++]=x%10,x/=10;}while(x);
    while(top) push(sta[--top]+'0'); push('
');
}
void update(int l,int r,int x,int &p) {
    if(!p) p=++tot;
    siz[p]++;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(x<=mid) update(l,mid,x,ls[p]);
    else update(mid+1,r,x,rs[p]);
}
int merge(int x,int y) {
    if(!x||!y) return x+y;
    int p=++tot;
    ls[p]=merge(ls[x],ls[y]);
    rs[p]=merge(rs[x],rs[y]);
    siz[p]=siz[ls[p]]+siz[rs[p]];
    return p;
}
int OK;
void query(int l,int r,int x,int y,int p) {
    if(!p||OK) return ;
    if(x<=l&&y>=r) {if(siz[p])OK=1; return ;}
    int mid=(l+r)>>1;
    if(x<=mid) query(l,mid,x,y,ls[p]);
    if(y>mid) query(mid+1,r,x,y,rs[p]);
}
struct sam {
    int ch[N][26],len[N<<1],lst,cnt,fa[N<<1],tag[N<<1];
    void init() {lst=cnt=1;}
    void insert(int x,int id) {
        int p=lst,np=++cnt,q,nq;
        lst=np; len[np]=len[p]+1; tag[np]=id;
        for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
        if(!p) fa[np]=1;
        else {
            q=ch[p][x];
            if(len[q]==len[p]+1) fa[np]=q;
            else {
                nq=++cnt; 
                len[nq]=len[p]+1; tag[nq]=tag[q];
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];
                fa[np]=fa[q]=nq;
                for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
            }
        }
    }
    void kero() {
        int i;
        for(i=1;i<=cnt;i++) ke[len[i]]++;
        for(i=1;i<=cnt;i++) ke[i]+=ke[i-1];
        for(i=cnt;i;i--) ro[ke[len[i]]--]=i;
        for(i=cnt;i>1;i--) {
            int p=ro[i]; root[fa[p]]=merge(root[fa[p]],root[p]);
        }
    }
    void run() {
        int i,p=1,now=0;
        for(i=1;i<=m;i++) {
            int x=w2[i]-'a';
            if(ch[p][x]) {
                p=ch[p][x]; now++;
            }else {
                for(;p&&!ch[p][x];p=fa[p]) ;
                if(!p) {p=1; now=0; h[i]=0; continue;}
                else {
                    now=len[p]+1; p=ch[p][x];
                }
            }
            if(L!=1||R!=n) {
                while(1) {
                    OK=0;
                    query(1,n,L+now-1,R,root[p]);
                    if(OK) break;
                    now--; if(now==len[fa[p]]) p=fa[p];
                    if(!p) {
                        p=1; now=0; break;
                    }
                }
            }
            h[i]=now;
        }
    }
    void get_ans() {
        ll ans=0; int i;
        for(i=2;i<=cnt;i++) {
            ans+=max(0,len[i]-max(len[fa[i]],h[tag[i]]));
        }
        write(ans);
        for(i=1;i<=cnt;i++) memset(ch[i],0,sizeof(ch[i])),len[i]=tag[i]=fa[i]=0;
    }
}S,T;
int main() {
    char s=nc();
    while(s<'a'||s>'z') s=nc();
    S.init();
    int i;
    for(;s>='a'&&s<='z';s=nc()) {
        w1[++n]=s;
    }
    for(i=1;i<=n;i++) {
        S.insert(w1[i]-'a',i);
        update(1,n,i,root[S.lst]);
    }
    S.kero();
    int q;
    q=rd();
    while(q--) {
        T.init();
        s=nc();
        m=0;
        while(s<'a'||s>'z') s=nc();
        for(;s>='a'&&s<='z';s=nc()) {
            w2[++m]=s;
        }
        L=rd(); R=rd();
        for(i=1;i<=m;i++) T.insert(w2[i]-'a',i);
        S.run();
        T.get_ans();
    }
    fwrite(pbuf,1,pp-pbuf,stdout);
}
原文地址:https://www.cnblogs.com/suika/p/10205412.html