单词「TJOI 2013」

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

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

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

对于全部数据,\(1\le N\le 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\) 不如到最后文章遍历结束后只需跳一次fail链加\(k\)

显然这么做是不会产生错误的 在文章遍历过程中每次给当前指针的节点tag+1即可 最后再给每个有tag的节点跳一次fail就统计完了

【代码】

#include <bits/stdc++.h>
#define re register
using namespace std;

int ans[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() {
        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]++;
        }
    } 
    
    inline void getans() {
        for (re int i = 1; i <= tot; i++) {
            if (val[i]) {
                for (re int j = i; j; j = fail[j]) {
                    f[j] += val[i];
                }
            }
        }
        for (re int i = 1; i <= tot; i++) {
            for (re int j = 0; j < tag[i].size(); j++) {
                ans[tag[i][j]] = f[i];
            }
        }
    }
} T;

int n, m, len;
string s[1000005];

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;
}

猜你喜欢

转载自www.cnblogs.com/ak-dream/p/AK_DREAM37.html