【TJOI2013】单词

Description:

小张最近在忙毕业设计,所以一直在读论文。一篇论文是由许多单词组成的。
但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。
单词数不超过200
单词总长度不超过1000000

题解:

这题我能想到的做法有三个。

第一个是最朴素的AC自动机。

我们需要清楚知道AC自动机的fail链的定义:走到x所代表的字符串有出现的最长的一个后缀。

在原trie上往子树走可以看作往后面加字符,在fail树上,往子树中走相当于往前加字符。

对每个点求出其是多少个单词的前缀,那么单词所对应点的fail子树的这个和就是答案。

第二个是很容易想的后缀自动机。

这个广义后缀自动机需要宽搜建,不然空点的问题就需要重新跑一遍了。

第三个是把全部的单词拼在一起,用特殊符号间隔,前后缀数组,求height,再用线段树二分的性质去搞。

因为单词数很小,所以这个方法也是可以的。

Code(AC自动机):

#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int N = 1e6 + 5;

int n, m, ed[N]; char str[N];
int son[N][26], fail[N], d[N], s[N], tot = 1;

int main() {
    scanf("%d", &n);
    fo(i, 1, n) {
        scanf("%s", str + 1); m = strlen(str + 1);
        int x = 1;
        fo(j, 1, m) {
            int c = str[j] - 'a';
            if(!son[x][c]) son[x][c] = ++ tot;
            x = son[x][c]; s[x] ++;
        }
        ed[i] = x;
    }
    d[0] = d[1] = 1;
    fo(i, 0, 25) son[0][i] = 1;
    fo(i, 1, d[0]) {
        int x = d[i];
        fo(j, 0, 25) if(son[x][j]) {
            int y = son[x][j], z = fail[x];
            while(!son[z][j]) z = fail[z];
            fail[y] = son[z][j]; d[++ d[0]] = y;
        }
    }
    fd(i, d[0], 1) s[fail[d[i]]] += s[d[i]];
    fo(i, 1, n) printf("%d\n", s[ed[i]]);
}

Code(后缀自动机):

#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int N = 2e6 + 5;

int last[N], ed[N], s[N];

struct Trie {
    int tot, son[N][26];
} t;

struct suffix_automation {
    int la, tot, son[N][26], fa[N], dep[N];
    #define push(x) dep[++ tot] = x;
    void add(int c) {
        push(dep[la] + 1);
        int p = la, np = tot; 
        for(; p && !son[p][c]; p = fa[p]) son[p][c] = np;
        if(!p) fa[np] = 1; else {
            int q = son[p][c];
            if(dep[p] + 1 < dep[q]) {
                push(dep[p] + 1); int nq = tot;
                memcpy(son[nq], son[q], sizeof son[q]);
                fa[nq] = fa[q]; fa[q] = fa[np] = nq;
                for(; son[p][c] == q; p = fa[p]) son[p][c] = nq;
            } else fa[np] = q;
        }
        la = np;
    }
    int d[N], r[N];
    void Turpo() {
        fo(i, 1, tot) r[fa[i]] ++;
        fo(i, 1, tot) if(!r[i]) d[++ d[0]] = i;
        fo(i, 1, d[0]) {
            int x = d[i];
            if(!(-- r[fa[x]])) d[++ d[0]] = fa[x];
            s[fa[x]] += s[x];
        }
    }
} suf; 

int n, m, d[N], ss[N]; char str[N];

int main() {
    suf.tot = t.tot = last[1] = 1;
    scanf("%d", &n);
    fo(i, 1, n) {
        scanf("%s", str + 1); m = strlen(str + 1);
        int x = 1;
        fo(j, 1, m) {
            int c = str[j] - 'a';
            if(!t.son[x][c]) t.son[x][c] = ++ t.tot;
            x = t.son[x][c];
            ss[x] ++;
        }
        ed[i] = x;
    }
    d[0] = d[1] = 1;
    fo(i, 1, d[0]) {
        int x = d[i];
        fo(j, 0, 25) if(t.son[x][j]) {
            d[++ d[0]] = t.son[x][j];
            suf.la = last[x];
            suf.add(j);
            last[t.son[x][j]] = suf.la;
            s[last[t.son[x][j]]] += ss[t.son[x][j]];
        }
    }
    suf.Turpo();
    fo(i, 1, n) printf("%d\n", s[last[ed[i]]]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值