牛客练习题 AC自动机 String (强制 离线AC自动机)

26 篇文章 0 订阅
20 篇文章 0 订阅

牛客练习题 AC自动机 String (强制 离线AC自动机)

嘛,这题TLE了我一下午,最终在晚上终于搞出来了!!!

基本思想是AC自动机,但由于题目是询问中穿插了字符串的添加,所以我们不得不将其离线(因为每次添加都需要更新fail指针,这样时间复杂度太大了),然后倒序处理询问,对之前添加进去的一一删去,得出答案后即可输出。

开始的时候就是普通树状数组的操作,我交的代码TLE是因为我没有做求单词个数过程的优化,我们建立的fail指针并不是每个都指向一个单词的结尾的,所以我们要新建一个last数组对替换fail指针,使得每一个last指针都从一个单词的结尾指向另一个单词的结尾,然后我们计算单词数量的时候时间复杂度就会少很多。于是这样我就过了。。。。嘤嘤嘤

上代码:

#pragma GCC optimize("O2")
#include <bits/stdc++.h>
using namespace std;  
const int maxn=2e5+1;
char str[maxn];
int n,m,tot=0;
int trie[maxn][30],pos[30*maxn],fail[30*maxn],ans[maxn],last[30*maxn];//加了一个last数组终于过了!!!
struct op{
    int en_pos,type;
    string s;
}pro[maxn];
inline int build(){
    int l=strlen(str),root=0;
    for(int i=0;i<l;++i){
        int id=str[i]-'a';
        if(!trie[root][id])
            trie[root][id]=++tot;
        root=trie[root][id];
    }
    pos[root]++;
return root;
}
inline void Fail(){
    queue<int> Q;
    for(int i=0;i<26;++i)
    if(trie[0][i])
        Q.push(trie[0][i]),fail[trie[0][i]]=0;
    while(!Q.empty()){
        int x=Q.front();
        Q.pop();
        for(int i=0;i<26;++i){
            int son=trie[x][i];
            if(son)
                fail[son]=trie[fail[x]][i],Q.push(son);
            else
                trie[x][i]=trie[fail[x]][i];
            last[son]=pos[fail[son]]?fail[son]:last[fail[son]];
        }
    }
}
inline int cal(int t){
    int x=0;
    while(t){
        x+=pos[t];
        t=last[t];
    }
return x;
}
inline int getans(int now){
    int l=pro[now].s.length(),ans=0,root=0,tt;
    for(int i=0;i<l;++i){
        int id=pro[now].s[i]-'a';
        while(root && !trie[root][id])
            root=fail[root];
        root=trie[root][id];
        if(pos[root])
            ans+=cal(root);
        else if(fail[root])
            ans+=cal(fail[root]);
    }
return ans;
}
void init(){
    for(int i=0;i<=tot;++i)
        fill(trie[i],trie[i]+26,0);
    fill(pos,pos+1+tot,0);
    fill(last,last+1+tot,0);
    tot=0;
}
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){
       if(ch=='-')w=-1;ch=getchar();
    }
   while(ch>='0'&&ch<='9') 
        s=s*10+ch-'0',ch=getchar();
   return s*w;
}
int main(){
    int t;
    t=read();
    while(t--){
        n=read(),m=read();
        for(int i=1;i<=n;++i){
            scanf("%s",str);
            build();
        }
        for(int i=1;i<=m;++i){
            pro[i].type=read();
            scanf("%s",str);
            if(pro[i].type==1)
                pro[i].en_pos=build();
            else 
                pro[i].s=str;
        }
        Fail();
        // debug();
        for(int i=m;i>0;--i){
            if(pro[i].type==1){
                int ppos=pro[i].en_pos;
                pos[ppos]--;
            }
            else
                ans[i]=getans(i);
        }
        for(int i=1;i<=m;++i)
        if(pro[i].type==2)
            printf("%d\n",ans[i]);
        init();
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值