洛谷P3796:AC自动机模板(加强版)

2020.6.13
声明本文借用了@hyfhaha 的题解思想。

前天回学校看了趟老师,都还是那个样子,一点没变。老师见到我们也很高兴,把我们留到了晚自习下课才放走。A-Level的班问cs的准备工作我还小小地安利了一下noip,嘻嘻,希望能够给学弟学妹一点准备。今年这个走势确实不太好,看到谭老师对下届忧心忡忡的样子真的很难过。一方面是大环境,一方面是,七中从我们这届之后录取结果就有点差强人意了,虽然这届总有几个20-30的,但是我们那届可是整个班一半人都到了美国前30大学的,没有前30的专业也都是最顶尖的,方的bu生物工程,wsl的艺术学院,lyx的动物医学都是顶尖专业。可惜了,老师们也经常说18届毕业之后再也见不到我们这样的学生了,可能跟机构有关,可能跟人有关,我们那届进来的人像我这样曲折的并不在少数,也可能跟不断贬值的sat分数有关,不知道什么原因,还是希望学校能迎接下一段辉煌,有了更好的结果,我们的退出也更有意义吧。

ac自动机,这道题的白板思路就是暴力匹配,但是看到1e6,多组数据,我认为100%超时,然后有人告诉我这个可以在fail树上操作,还是没想出来。看了题解才知道原来是可以边匹配边搞,就是对于trie[cur][val]每一个都有唯一确定下标的子串,使得在匹配a的时候如果包含了b在路径上,b必为a的子串,只要将下标所代表的trie的结束节点标上下标,匹配的时候直接加就可以了,很巧妙的办法。本来想用char数组的,然而发现开不下,还是算了,用str吧。
代码:

#include <bits/stdc++.h>
using namespace std;
#define limit (500000 + 5)//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-6
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a,b) pair<a,b>
#define rep(i, a, b) for(int i = a; i <= b ; ++i)
#define per(i, a, b) for(int i = b ; i >= a ; --i)
#define mint(a,b,c) min(min(a,b), c)
#define MOD 998244353
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\data.txt", "rt", stdin)
typedef long long ll;
typedef unsigned long long ull;
ll read(){
    ll sign = 1, x = 0;char s = getchar();
    while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();}
    while(s >= '0' && s <= '9'){x = x * 10 + s - '0';s = getchar();}
    return x * sign;
}//快读
void write(ll x){
    if(x < 0) putchar('-'),x = -x;
    if(x / 10) write(x / 10);
    putchar(x % 10 + '0');
}
int n,num[limit];

int ans[limit];
struct node{
    #define ALPHA 25
    int trie[limit][26], fail[limit],tot = 0;
    void clear(){
        memset(trie, 0 , sizeof(trie));
        memset(fail, 0 , sizeof(fail));
        memset(num, 0 , sizeof(num));
        memset(ans, 0 , sizeof(ans));
        tot = 0;
    }
    void insert(const char *s, int index){
        int root = 0;
        int len = strlen(s);
        rep(i , 0 ,len - 1){
            int val = s[i] - 'a';
            if(!trie[root][val])trie[root][val] = ++tot;//没有被访问过
            root = trie[root][val];
        }
        num[root] = index;
    }
    void build(){
        queue<int>q;
        rep(i ,0 , ALPHA){
            if(trie[0][i])fail[trie[0][i]] = 0, q.push(trie[0][i]);
        }
        while (q.size()){
            int u = q.front();
            q.pop();
            rep(i , 0 ,ALPHA){
                if(trie[u][i]){
                    fail[trie[u][i]] = trie[fail[u]][i];
                    q.push(trie[u][i]);
                }else{
                    trie[u][i] = trie[fail[u]][i];
                }
            }
        }
    }
    void operator[](const char * s){
        int len = strlen(s);
        int root = 0 ;
        rep(i, 0 , len - 1){
            root = trie[root][s[i] - 'a'];
            for(int t = root; t ; t = fail[t]){
                ++ans[num[t]];
            }
        }
    }
};
string str;
string collection[limit];
node AC_Automaton;//命名
int main() {
#ifdef LOCAL
    FOPEN;
#endif
    while(cin>>n && n){
        rep(i ,1, n){
            cin>>str;
            collection[i] = str;
            AC_Automaton.insert(str.c_str(), i);
        }
        int maxx = 0;
        cin>>str;
        AC_Automaton.build();
        AC_Automaton[str.c_str()];
        rep(i ,1, n){
            maxx = max(maxx, ans[i]);
        }
        write(maxx),putchar('\n');
        rep(i,1,n){
            if(ans[i] == maxx){
                cout<<collection[i]<<endl;
            }
        }
        AC_Automaton.clear();
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值