• 单词「TJOI 2013」


    【题目描述】
    某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。

    【输入格式】
    第一个一个整数 (N),表示有多少个单词,接下来 (N) 行每行一个单词。

    【输出格式】
    输出 (N) 个整数,第 (i) 行的数字表示第 (i) 个单词在文章中出现了多少次。

    对于全部数据,(1le Nle 200),所有单词长度的和不超过 (10^6),保证每个单词由小写字母组成。

    题解

    这道题题目意思让我看半天

    总之就是每个输入的单词同样也作为一篇文章 然后最后要你统计 每个词 在 所有词 中共出现了多少次

    比如样例

    3
    a
    aa
    aaa
    

    (aa)(a)中出现(0)次 在(aa)中出现(1)次 在(aaa)中出现(2)次 所以是(0+1+2=3)

    作为一道AC自动机的模板题,这道题在BZOJ上时限给了10s。。。

    实际上不用这么宽的时限 1s就够了

    首先先建好AC自动机 查询每个单词在文章中出现次数 一般我们会维护一个指针在AC自动机上跟着文章的字母走 然后每走一个字母都要暴力跳fail统计答案

    然而很容易发现这样做的弊端:如果重复经过了AC自动机的同一个节点(k)次 那么就要重复跳(k)次同样的fail链每次加(1) 不如到最后文章遍历结束后再进行统计

    所以就把沿路经过的节点(val+1),相当于打上一个标记 最后在fail树上累加一下(x)的子树和就是(x)代表的串出现多少次

    【代码】

    #include <bits/stdc++.h>
    #define re register
    using namespace std;
    
    int ans[1000005];
    
    int n, m, len;
    string s[1000005];
    
    struct AC_automaton{
    	int ch[1000005][30], fail[1000005], val[1000005], f[1000005], tot;
    	vector<int> tag[1000005];
    	
    	inline int o(char c) { return c - 'a'; } 
    	
    	inline void insert(string str, int id) {
    		int x = 0;
    		for (re int i = 0; i < str.size(); i++) {
    			if (!ch[x][o(str[i])]) {
    				ch[x][o(str[i])] = ++tot;
    			}
    			x = ch[x][o(str[i])];
    		}
    		tag[x].push_back(id);
    	}
    	
    	queue<int> q;
    	
    	inline void getfail() {
    		while (!q.empty()) q.pop();
    		for (re int i = 0; i < 26; i++) if (ch[0][i]) q.push(ch[0][i]);
    		while (!q.empty()) {
    			int x = q.front(); q.pop();
    			for (re int i = 0; i < 26; i++) {
    				if (ch[x][i]) fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]);
    				else ch[x][i] = ch[fail[x]][i];
    			}
    		}
    	}
    	
    	inline void solve(string str) {
    		int now = 0;
    		for (re int i = 0; i < str.size(); i++) {
    			now = ch[now][o(str[i])];
    			val[now]++;
    		}
    	} 
    	
    	int in[1000005], b[1000005], cnt;
    	
    	inline void toposort() {
    		while (!q.empty()) q.pop();
    		for (int i = 1; i <= tot; i++) {
    			in[fail[i]]++;
    		}		
    		for (int i = 0; i <= tot; i++) {
    			if (!in[i]) q.push(i);
    		}
    		while (!q.empty()) {
    			int x = q.front(); q.pop();
    			b[++cnt] = x;
    			in[fail[x]]--;
    			if (!in[fail[x]]) q.push(fail[x]);
    		}
    	}
    	
    	inline void getans() {
    		toposort(); //拓扑排序完后直接累加就可以了
    		for (int i = 1; i <= cnt; i++) {
    			int x = b[i];
    			if (x != 0) val[fail[x]] += val[x];
    		}
    		for (int i = 0; i <= tot; i++) {
    			for (int j = 0; j < tag[i].size(); j++) {
    				ans[tag[i][j]] += val[i];
    			}
    		}
    	}
    } T;
    
    int main() {
    	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    	cin >> n;
    	for (re int i = 1; i <= n; i++) {
    		cin >> s[i];
    		T.insert(s[i], i);
    	}
    	T.getfail();
    	for (re int i = 1; i <= n; i++) {
    		T.solve(s[i]);
    	}
    	T.getans();
    	for (re int i = 1; i <= n; i++) {
    		cout << ans[i] << endl;
    	}
    	return 0;
    }
    
  • 相关阅读:
    C# 重载,重写,代理,枚举实例
    关于C#7 新语法糖
    关于C# 异步
    经验
    C# 后台模块 Word 模板操作
    .Net 图片生成水印
    .Net XML操作
    .Net web 关于表单标题
    分页存储过程
    python 爬虫 猫眼视频榜单top100
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM37.html
Copyright © 2020-2023  润新知