• P3796 【模板】AC自动机(加强版)


    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <queue>
    #include <algorithm>
    #include <vector>
    using namespace std;
    struct Trie	{
    	int fail;
    	int son[26];
    	int end;
    } AC[1000005];
    struct Str {
    	int num, pos;
    } str[1000005];
    bool cmp(Str a, Str b) {
    	if(a.num != b.num) return a.num > b.num;
    	else return a.pos < b.pos;
    }
    int cnt = 0;
    void Clean(int x) {
    	AC[x].fail = AC[x].end = 0;
    	memset(AC[x].son, 0, sizeof(AC[x].son));
    }
    inline void Build(string s, int num) { //把模式串依次插入trie
    	int l = s.length();
    	int now = 0;
    	for(int i = 0; i < l; i++) {
    		if(AC[now].son[s[i] - 'a'] == 0) {
    			AC[now].son[s[i] - 'a'] = ++cnt;
    			Clean(cnt);//不初始化的话会mle,因为这是一个新的结点
    		}
    		now = AC[now].son[s[i] - 'a'];
    	}
    	AC[now].end = num;//以now为结尾的字符串在输入里的序号为num
    }
    void getFail() { //计算失配指针
    	queue<int> q;
    	for(int i = 0; i < 26; i++) { //单独处理第二层的失配指针
    		if(AC[0].son[i] != 0) { //根结点的这个儿子存在的话
    			AC[AC[0].son[i]].fail = 0;//因为比它深度更浅的只有根结点了
    			q.push(AC[0].son[i]);//入队准备进行BFS
    		}
    	}
    	while(!q.empty()) {
    		int u = q.front();
    		q.pop();
    		for(int i = 0; i < 26; i++) {//找当前结点的所有子结点,因为是BFS,所以上一层所有结点的fail指针都已经计算出来了,满足计算当前结点fail指针的条件
    			if(AC[u].son[i] != 0) {//如果这个子结点存在的话
    				AC[AC[u].son[i]].fail = AC[AC[u].fail].son[i];//当前结点子结点的失配指针设置为当前结点的fail指针指向的结点的所有子结点中和当前结点子结点值相同的结点
    				q.push(AC[u].son[i]);//入队
    			} else {
    				AC[u].son[i] = AC[AC[u].fail].son[i];//当前结点的子结点不存在,就把这个子结点设置为当前结点失配指针指向的结点的所有子结点中和当前结点子结点值相同的结点
    				//相当于走到了当前结点发现无路可走,就退而求其次,看看跳到从根到当前结点这个串的最长后缀的最后面能不能继续走下去
    				//注意此时当前结点fail指针指向的结点由于深度更浅,它的son[i]已经被更浅层的结点更新过了,所以可以拿过来用
    				//考虑这么一棵trie:0-1-2-3  0-2-3  0-3-4
    			}
    		}
    	}
    }
    void AC_Query(string s) { //对文本串进行匹配
    	int l = s.length();
    	int now  = 0;//一开始从树根 开始遍历
    	for(int i = 0; i < l; i++) {
    		now = AC[now].son[s[i] - 'a'];//这里的now遍历过的字符串路径实际上就是文本串
    		for(int t = now; t; t = AC[t].fail) {//直接将t指向失配指针以节约时间
    			str[AC[t].end].num++;//AC[t].end这个编号对应的字符串数目++
    		}
    	}
    }
    int main() {
    	freopen("data.txt", "r", stdin);
    	std::ios::sync_with_stdio(false);
    	cin.tie(0); 
    	cout.tie(0);
    	int n;
    	while(cin >> n && n) {
    		cnt = 0;
    		Clean(0);
    		vector<string> v;
    		v.push_back(" ");
    		for(int i = 1; i <= n; i++) {
    			string s;
    			cin >> s;
    			str[i].pos = i;
    			str[i].num = 0;//出现次数
    			Build(s, i);
    			v.push_back(s);
    		}
    		AC[0].fail = 0;
    		getFail();
    		string t;
    		cin >> t;
    		AC_Query(t);
    		sort(str + 1, str + n + 1, cmp);
    		int times = str[1].num;
    		cout << times << endl;
    		for(int i = 1; i <= n; i++) {
    			if(str[i].num == times) {
    				cout << v[str[i].pos] << endl;
    			} else {
    				break;
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Django 【第十二篇】Form组件进阶
    Django 【第十一篇】Form组件基础
    前端知识点总结
    vue组件续和前端工程化
    vue组件
    Vue视图下
    Vue实例与渲染
    BootStrap
    jQuery事件与动画
    dom操作 属性操作 样式操作
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/14491237.html
Copyright © 2020-2023  润新知