1. 程式人生 > 實用技巧 >「NOI2018」 你的名字

「NOI2018」 你的名字

怕是以後在也不能做非思維題了,要不然都想不出來啥了

其實早知道是線段樹合併,然後拍上一個維護 \(endpos\) 就開始坐以待斃了

Description

給定 \(S\) 和若干個詢問,每個詢問是 \(T,l,r\) 的形式,求 \(T\) 有多少個子串在 \(S[l\dots r]\) 中沒有出現過

\(|S|\le 5\times 10^5,\sum |T|\le 10^6\)

其中對於 \(68pts\) 的部分分滿足 \(l=1,r=|S|\)

Solution

首先考慮一個 \(n^2\) 的做法是對於每個 \(T\)\(SAM\) 然後跑可達性統計,然後兩個串一起跑,如果這個點在 \(T\)

裡面能跑到 \(S\) 裡面沒有就加上可達的點

貌似也沒啥優化的空間,那麼換一個思路,往 字尾樹 上面靠

直接統計還是不太行,那麼正難則反,用總的 \(T\) 上的減掉公共子串

具體就是求一下 \(l[i]\) 表示 \(T\)\(i\) 的字首有多長可以在 \(S\) 上面作為一段字尾出現

如果如果不考慮存在重複的情況,就把 \(T\) 放到 \(S\) 上面跑,每次不行了就跳 \(fa\)

(這裡深刻一個觀點:字尾樹上面點的深度基本上是沒啥用的,含義就可以輕鬆得證)

因為 \(fa\) 裡面的都是 \(x\) 的字尾,又要選最大值,所以每跳一下,長度減成現在點的 \(len\)

那麼這就得到了一個 \(68pts\)

的做法


接著考慮怎麼做不是全串的情況,先求所有的 \(endpos\) 集合

(如果對字尾樹的相關熟練的話,其實這步是很好說的)

然後考慮對於 \([l\dots r]\) 的限制怎麼寫

然而並不能每次暴力重建,但是每次只是找兒子,跳父親,找當前點在 \([l,r]\) 內的最大 \(len\)

第一個其實對應的是 \(endpos\) 有沒有在 \([l,r]\) 之間有點

第二個是跳父親,因為父親的 \(endpos\) 是母集,隨便跳

最後一個比較噁心,想清楚了是 \(min(len[i],now-max(now,pos)+1)\)

\(pos\) 是指在 \([l,r]\) 內的最大的 \(endpos\)

下標,這個可以用線段樹求出來

還是一樣的跑就行了

本文中加粗的部分是瓶頸,觀察字尾自動機在什麼時候用和跑 \(pos\) 這兩個套路確實厲害

彷彿 \(CF\) 上有類似套路題?

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define reg register
namespace yspm{
    inline int read(){
        int res=0,f=1; char k;
        while(!isdigit(k=getchar())) if(k=='-') f=-1;
        while(isdigit(k)) res=res*10+k-'0',k=getchar();
        return res*f;
    }
    const int N=5e5+10,M=1e6+10,SZ=M*40;
    int ls[SZ],rs[SZ],sum[SZ],tot,rt[M];
    inline int max(int x,int y){return x>y?x:y;}
    struct Saffix_Automation{
        int son[M][26],len[M],fa[M],tot,las,id[M];
        inline void extend(int x,int pos,bool fl){
            int tmp=las,np=las=++tot; len[np]=len[tmp]+1; id[np]=pos;
            while(!son[tmp][x]) son[tmp][x]=np,tmp=fa[tmp];
            if(!tmp) return fa[np]=1,void();
            int q=son[tmp][x]; if(len[q]==len[tmp]+1) return fa[np]=q,void();
            int clone=++tot; len[clone]=len[tmp]+1; fa[clone]=fa[q]; fa[q]=fa[np]=clone; 
            for(reg int i=0;i<26;++i) son[clone][i]=son[q][i]; if(fl) id[clone]=id[np];
            while(son[tmp][x]==q) son[tmp][x]=clone,tmp=fa[tmp];  
            return ;    
        }
        inline void clear(){
            for(reg int i=1;i<=tot;++i) fa[i]=len[i]=id[i]=0,memset(son[i],0,sizeof(son[i])); las=tot=1; 
            return ;
        }
        inline void init(){tot=1; las=1; return;}
        inline int num(int x){return len[x]-len[fa[x]];}
    }S1,S2;
    
    inline void push_up(int x){return sum[x]=sum[ls[x]]+sum[rs[x]],void();}
    inline void upd(int &p,int l,int r,int pos){
        if(!p) p=++tot; 
        if(l==r) return ++sum[p],void();
        int mid=(l+r)>>1; if(pos<=mid) upd(ls[p],l,mid,pos); else upd(rs[p],mid+1,r,pos);
        return push_up(p);
    }
    inline int merge(int x,int y,int l,int r){
        if(!x||!y) return x+y;
        if(l==r) return sum[x]|=sum[y],x;
        int mid=(l+r)>>1,p=++tot;
        ls[p]=merge(ls[x],ls[y],l,mid); rs[p]=merge(rs[x],rs[y],mid+1,r);
        return push_up(p),p;
    }
    ll ans;
    int head[M],cnt,mx[M],now,nowlen,len,slen; char s[M];
    struct edge{int to,nxt;}e[M];
    inline void add(int u,int v){
        e[++cnt].to=v; e[cnt].nxt=head[u];
        return head[u]=cnt,void();
    }
    inline void dfs(int x){
        if(S1.id[x]) upd(rt[x],1,slen,S1.id[x]);
        for(reg int i=head[x];i;i=e[i].nxt) dfs(e[i].to),rt[x]=merge(rt[x],rt[e[i].to],1,slen);
        return ;
    }
    inline int query(int p,int l,int r,int st,int ed){
        if(!p) return 0;
        if(st<=l&&r<=ed) return sum[p];
        int mid=(l+r)>>1; 
        if(st<=mid&&ls[p]&&query(ls[p],l,mid,st,ed)) return 1;
        if(ed>mid&&rs[p]&&query(rs[p],mid+1,r,st,ed)) return 1;  
        return 0;
    }
    signed main(){
        scanf("%s",s+1); slen=strlen(s+1); S1.init();
        for(reg int i=1;i<=slen;++i) S1.extend(s[i]-'a',i,0);
        for(reg int i=1;i<=S1.tot;++i) add(S1.fa[i],i); dfs(1);
        int T=read(),l,r; while(T--){
            S2.clear(); scanf("%s",s+1); len=strlen(s+1); l=read(); r=read();  
            for(reg int i=1;i<=len;++i) S2.extend(s[i]-'a',i,1);
            for(reg int i=1;i<=len;++i) mx[i]=0; ans=0; now=1; nowlen=0;
            for(reg int i=1;i<=len;++i){
                while(1){
                    int nxt=S1.son[now][s[i]-'a']; 
                    if(nxt&&query(rt[nxt],1,slen,l+nowlen,r)){nowlen++,now=nxt; break;}
                    if(!nowlen) break; --nowlen;
                    if(nowlen==S1.len[S1.fa[now]]) now=S1.fa[now];
                } mx[i]=nowlen; 
            }
            for(reg int i=2;i<=S2.tot;++i) ans+=max(0,S2.len[i]-max(S2.len[S2.fa[i]],mx[S2.id[i]]));
            printf("%lld\n",ans); 
        }
        return 0;
    }
}
signed main(){return yspm::main();}