洛谷P3966 [TJOI2013]单词 题解

随声附和 提交于 2020-02-07 08:16:00

题目链接

分析:

nn个字符串跑一边AC自动机,建立fail树(即由fail指针组成的树),每个点的点权为在Trie树上经过这个点的字符串的数量。则每个点所代表的的字符串在所有单词中出现次数显然为这个点在fail树上的子树的点权和,跑一遍树形DP,记录一下每个单词的位置输出即可。

具体看代码

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 220,maxm = 1e6 + 50;
int n,cnt,l,r,a[maxn],f[maxm],nxt[maxm],t[maxm][26],q[maxm];
char s[maxm];
void insert(char *s,int x){
    int now = 0,len = strlen(s);
    for(int i = 0; i < len; i ++){
        if(!t[now][s[i] - 'a']) t[now][s[i] - 'a'] = ++cnt;
        now = t[now][s[i] - 'a'],f[now] ++;//经过字符串加一
    }
    a[x] = now;//记录每个字符串位置
}
void build(){
    l = 1,r = 0;
    for(int i = 0; i < 26; i ++) if(t[0][i]) nxt[t[0][i]] = 0,r ++,q[r] = t[0][i];
    while(l <= r){
        int now = q[l];
        l ++;
        for(int i = 0; i < 26; i ++){
            if(t[now][i]) nxt[t[now][i]] = t[nxt[now]][i],r ++,q[r] = t[now][i];
            else t[now][i] = t[nxt[now]][i];
        }
    }
}
int main(){
    cin >> n;
    for(int i = 1; i <= n; i ++){
        scanf("%s",s);
        insert(s,i);
    }
    build();
    for(int i = r; i >= 1; i --) f[nxt[q[i]]] += f[q[i]];//由于建树时相当于已经把点在队列里按深度排序,可以直接循环,不需要dfs
    for(int i = 1; i <= n; i ++) printf("%d\n",f[a[i]]);
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!