ac自动机fail树上按询问建立上跳指针——cf963D

解法看着吓人,其实就是为了优化ac自动机上暴力跳fail指针。。

另外这题对于复杂度的分析很有学习价值

/*
给定一个母串s,再给定n个询问(k,m) 
对于每个询问,求出长度最小的t,使t是s的子串,且m作为子串在t中出现了m次

对多串建立ac自动机,然后用s去匹配,把所有询问的出现位置都用vector保存下来
然后对应每个询问的k进行更新答案 

为了保证复杂度:在跳fail不能暴力向上跳,应该直接用一个指针pre跳到上一个带有询问的点
这样每次向上跳都让某个询问的vector更新进一个新的值
由于最多有sqrt(n)个不同的询问串长度,所以以每个s[i]为结尾的可匹配串也只有sqrt(n)个,
即最多在fail树上跳nsqrt(n)次 
*/
#include
using namespace std;
#define N 200005

struct Query{
    vector<int>pos;
    int k,len;
}q[N];
char s[N],buf[N];
int n;

struct Trie{
    int nxt[N][26],fail[N],id[N],pre[N];
    int root,L;
    int newnode(){
        memset(nxt[L],-1,sizeof nxt[L]);
        id[L]=0;
        return L++;
    }
    void init(){L=0;root=newnode();}
    void insert(char buf[],int ID){
        int len=strlen(buf);
        int now=root;
        for(int i=0;i){
            if(nxt[now][buf[i]-'a']==-1)
                nxt[now][buf[i]-'a']=newnode();
            now=nxt[now][buf[i]-'a'];
        }
        id[now]=ID;
    }
    void build(){
        queue<int>q;
        fail[root]=root;
        for(int i=0;i<26;i++)
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        pre[root]=0;
        
        while(q.size()){
            int now=q.front();q.pop();//此时now的fail已经建立好 
            if(id[fail[now]]!=0)//找上一个询问的位置 
                pre[now]=fail[now];
            else 
                pre[now]=pre[fail[now]];
            
            for(int i=0;i<26;i++)
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
        }
    }
    
    void query(char *s){
        int len=strlen(s);
        int now=root;
        for(int i=0;i){
            now=nxt[now][s[i]-'a'];
            //通过pre向上跳
            int p=now;
            while(p){
                q[id[p]].pos.push_back(i);
                p=pre[p];
            } 
        }
        
    }
}ac;

int main(){
    ac.init();
    scanf("%s",s);
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d%s",&q[i].k,buf);
        q[i].len=strlen(buf);
        ac.insert(buf,i);
    }
    ac.build();
    ac.query(s);

//for(int i=1;i<=ac.L;i++)
//    cout<//处理每个询问 
    for(int i=1;i<=n;i++){
//cout<
        if(q[i].pos.size()<q[i].k){
            puts("-1");continue;
        }
        
        int ans=0x3f3f3f3f;
        for(int j=q[i].k-1;j){
            ans=min(ans,q[i].pos[j]-q[i].pos[j-q[i].k+1]+q[i].len);
        }
        cout<'\n'; 
    }
}
/*

aaabbbbaaabababab
27
2 aaabbbbaaaba
2 baaabab
1 abbbbaaabab
1 aabbbbaaabab
6 a
1 aaabbbbaaabababab
2 aaba
2 abbbba
5 aa
2 aaabbbb
2 abababa
3 aba
2 baaa
2 bbaaababa
1 aaabab
1 abbb
1 bbbbaaabababa
1 baaab
1 abbbbaaabababa
1 aaababa
1 ababab
2 abb
2 baaabababa
1 bbaaabababa
2 aaabb
1 abababab
4 bab



*/

 

你可能感兴趣的:(ac自动机fail树上按询问建立上跳指针——cf963D)