葫芦的考验之定位子串

葫芦的考验之定位子串

思路

后缀自动机+倍增
直接在建立的后缀树上进行查找,因为孩子和父亲一定是具有相同的后缀的,所以只需要后缀的长度足够就可以了。

代码

#include <bits/stdc++.h>
using namespace std;
const int M=2.5e5+5;

struct Suffix_Auto {
    char s[M];
    int np=1,tot=1,n;
    int ch[M<<1][26],fa[M<<1],len[M<<1],siz[M<<1],d[M];
    void insert(int c,int id) {
        int p=np;np=++tot;
        len[np]=len[p]+1;siz[np]=1;d[id]=tot;
        for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
        if(!p)fa[np]=1;
        else {
            int q=ch[p][c];
            if(len[p]+1==len[q])fa[np]=q;
            else {
                int nq=++tot;len[nq]=len[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
            }
        }
    }
    
    int h[M<<1],e[M<<1],ne[M<<1],cnt;
    void add(int from,int to) {
        e[++cnt]=to;  ne[cnt]=h[from];  h[from]=cnt;
    }
    
    //感觉更像一个后缀树吧,树上的后缀都是相同的
    int f[M<<1][21];
    void dfs(int now,int Fa) {
        f[now][0]=Fa;
        for(int i=1;i<=20;i++)
            f[now][i]=f[f[now][i-1]][i-1];
        for(int i=h[now];i;i=ne[i]) {
            int to=e[i];
            dfs(to,now);
            siz[now]+=siz[to];
        }
    }
    
    void build() {
        scanf("%s",s+1);
        n=strlen(s+1);
        for(int i=1;i<=n;i++)insert(s[i]-'a',i);
        for(int i=2;i<=tot;i++)add(fa[i],i);
        dfs(1,0);
    }
    
    int query(int l,int r) {//后缀树上查找,上面的一定和这个的后缀相同,所以只需要长度够就可以了
        int Len=r-l+1;
        int p=d[r];
        for(int i=20;i>=0;i--)
            if(len[f[p][i]]>=Len)p=f[p][i];
        return siz[p];
    }
    
    void solve() {
        int m;cin>>m;
        while(m--) {
            int l,r;
            cin>>l>>r;
            
            cout<<query(l,r)<<'\n';
        }
    }
}SAM;

int main() {
    SAM.build();
    SAM.solve();
    return 0;
}
posted @ 2023-02-24 21:08  basicecho  阅读(46)  评论(0)    收藏  举报