https://www.luogu.org/problemnew/show/P3966

因为文本串就是字典本身,所以这个和平时的AC自动机不太一样。平时的query要沿着fail树把子树的出现次数依次统计。但是这个的query就是对每个字典里的字符串搞一次。所以就直接按广搜的顺序反过来树形dp统计出子树中的出现次数,直接回答。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; const int MAXN = 1e6;
const int MAXM = 2e2; int idx[MAXM + 5]; struct Trie {
int trie[MAXN + 5][26], fail[MAXN + 5], cnt[MAXN + 5], que[MAXN + 5];
int root, top;
int newnode() {
for(int i = 0; i < 26; i++)
trie[top][i] = -1;
cnt[top++] = 0;
return top - 1;
} void init() {
top = 0;
root = newnode();
} void insert(char buf[], int id) {
int len = strlen(buf);
int cur = root;
for(int i = 0; i < len; i++) {
int u = buf[i] - 'a';
if(trie[cur][u] == -1)
trie[cur][u] = newnode();
cur = trie[cur][u];
cnt[cur]++;
}
idx[id] = cur;
} void build() {
int head = 0, tail = 0;
fail[root] = root;
for(int i = 0; i < 26; i++) {
if(trie[root][i] == -1)
trie[root][i] = root;
else {
fail[trie[root][i]] = root;
que[tail++] = trie[root][i];
}
}
while(head < tail) {
int cur = que[head++];
for(int i = 0; i < 26; i++) {
if(trie[cur][i] == -1)
trie[cur][i] = trie[fail[cur]][i];
else {
fail[trie[cur][i]] = trie[fail[cur]][i];
que[tail++] = trie[cur][i];
}
}
}
for(int i = tail - 1; i >= 0; i--) {
cnt[fail[que[i]]] += cnt[que[i]];
}
}
}; char buf[MAXN + 5]; Trie ac; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
//freopen("Yinku.out","w",stdout);
#endif // Yinku
int n;
scanf("%d", &n);
ac.init();
for(int i = 0; i < n; i++) {
scanf("%s", buf);
ac.insert(buf, i);
}
ac.build();
for(int i = 0; i < n; i++)
printf("%d\n", ac.cnt[idx[i]]);
}

这种写法是,每次文本串遇到一个模式串,就会给这个模式串+1次出现次数。问题是怎么优化重复的文本串呢?把文本串插入另一个trie里面就可以知道他们的重复数。然后每次就给每个模式串都贡献一个重数就等价了。这样最好理解。优化的地方在于同样的文本串不用多次匹配。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; const int MAXN = 1e6;
const int MAXM = 2e2; int ans[MAXM + 5];
int aidx[MAXN + 5][MAXM + 5]; struct Trie {
int trie[MAXN + 5][26], fail[MAXN + 5], cnt[MAXN + 5], que[MAXN + 5];
//本题多使用的一个是文本串重复计数器tcnt,但是在这里文本一定和模式一样就可以合并到模式重复计数器cnt里,一个文本防重vis
int tcnt[MAXN + 5];
bool vis[MAXN + 5]; int root, top;
int newnode() {
for(int i = 0; i < 26; i++)
trie[top][i] = -1;
cnt[top++] = 0;
return top - 1;
} void init() {
top = 0;
root = newnode();
} void insert(char buf[], int id) {
int len = strlen(buf);
int cur = root;
for(int i = 0; i < len; i++) {
int u = buf[i] - 'a';
if(trie[cur][u] == -1)
trie[cur][u] = newnode();
cur = trie[cur][u];
}
cnt[cur]++;
//本题中文本可以一并插入,干脆合并成一个cnt,可以在同一个trie中,否则应该分开插入两个trie
aidx[cur][cnt[cur]-1] = id;
} void build() {
int head = 0, tail = 0;
fail[root] = root;
for(int i = 0; i < 26; i++) {
if(trie[root][i] == -1)
trie[root][i] = root;
else {
fail[trie[root][i]] = root;
que[tail++] = trie[root][i];
}
}
while(head < tail) {
int cur = que[head++];
for(int i = 0; i < 26; i++) {
if(trie[cur][i] == -1)
trie[cur][i] = trie[fail[cur]][i];
else {
fail[trie[cur][i]] = trie[fail[cur]][i];
que[tail++] = trie[cur][i];
}
}
}
} void query(char buf[]) {
//该文本串匹配了多少次模式串
/*int len=strlen(buf);
int cur=root;
int res=0;
for(int i=0;i<len;i++){
cur=trie[cur][buf[i]-'a'];
int temp=cur;
while(temp!=root){
//这里既然是+=意思就是有多少个模式串匹配了都算,而且多次匹配就多次算,否则每次就把他清0就可以了
res+=cnt[temp];
temp=fail[temp];
}
}
return res;*/
//该种文本串对所有模式串贡献了几次?
//插好所有文本之后得到文本的重数,对每个文本在trie中O(n)找出他的重数以及他有没有被vis
//没有被vis的话就对每个匹配的模式贡献对应的重数
int len = strlen(buf);
int cur = root;
for(int i = 0; i < len; i++) {
cur = trie[cur][buf[i] - 'a'];
}
if(vis[cur])
return;
int cf = cnt[cur];
vis[cur]=1; cur = root;
for(int i = 0; i < len; i++) {
cur = trie[cur][buf[i] - 'a'];
int temp = cur;
while(temp != root) {
for(int j = 0; j < cnt[temp]; j++) {
ans[aidx[temp][j]]+=cf;
}
temp = fail[temp];
}
}
}
}; char buf[MAXM+5][MAXN + 5]; Trie ac; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
//freopen("Yinku.out","w",stdout);
#endif // Yinku
int n;
scanf("%d", &n);
ac.init();
for(int i = 0; i < n; i++) {
scanf("%s", buf[i]);
ac.insert(buf[i], i);
}
ac.build();
for(int i = 0; i < n; i++) {
ac.query(buf[i]);
} for(int i = 0; i < n; i++)
printf("%d\n", ans[i]);
}

洛谷 - P3966 - 单词 - AC自动机的更多相关文章

  1. 洛谷P3966 单词 [TJOI2013] AC自动机

    正解:AC自动机 解题报告: 传送门! 先来提供一个40pts错解QAQ 首先看到这题就会想到AC自动机板子题2鸭!然后就照着那题的套路打一下,随便改一点儿,简单来说就是每次经过一个节点都要++,然后 ...

  2. 洛谷P3808 & P3796 AC自动机模板

    题目:P3808:https://www.luogu.org/problemnew/show/P3808 P3796:https://www.luogu.org/problemnew/show/P37 ...

  3. 洛谷.3121.审查(AC自动机 链表)

    题目链接 //删掉一个单词需要前移一段位置,用链表维护就好了 复杂度O(sum(len)) #include <cstdio> #include <cstring> #defi ...

  4. 洛谷 - P2444 - 病毒 - AC自动机

    https://www.luogu.org/problemnew/show/P2444 有点恶心,不太明白fail的意义. #include<bits/stdc++.h> using na ...

  5. 洛谷 P1101 单词方阵

    题目链接 https://www.luogu.org/problemnew/show/P1101 题目描述 给一n×n的字母方阵,内可能蕴含多个"yizhong"单词.单词在方阵中 ...

  6. 洛谷 P1381 单词背诵

    洛谷 P1381 单词背诵 洛谷传送门 题目描述 灵梦有n个单词想要背,但她想通过一篇文章中的一段来记住这些单词. 文章由m个单词构成,她想在文章中找出连续的一段,其中包含最多的她想要背的单词(重复的 ...

  7. 洛谷P1808 单词分类_NOI导刊2011提高(01) 字符串排序

    洛谷P1808 单词分类_NOI导刊2011提高(01) 题目描述 Oliver为了学好英语决定苦背单词,但很快他发现要直接记住杂乱无章的单词非常困难,他决定对单词进行分类. 两个单词可以分为一类当且 ...

  8. 洛谷P3966 [TJOI2013]单词(AC自动机)

    题目描述 小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次. 输入输出格式 输入格式: 第一行一个整数N,表 ...

  9. 洛谷P3966 [TJOI2013]单词(后缀自动机)

    传送门 统计单词出现次数……为啥大家都是写AC自动机的嘞……明明后缀自动机也能做的说…… 统计出现次数这个就直接按长度排序然后做个dp就好,这是SAM的板子的要求啊,不提了 然后考虑怎么让所有串之间隔 ...

随机推荐

  1. Linux中的du和df命令

    现在也将前阵子学习到du/df两个命令总结一下吧.前阵子测试工作中有遇到过由于磁盘空间满导致程序无法执行到情况,所以使用了df和du两个命令. du查看目录大小,df查看磁盘使用情况.我常使用的命令( ...

  2. mysql查询结果自动生成序列号

  3. 在与SQL Server 建立 连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器

  4. RS485总线典型电路介绍

    一.RS485总线介绍: RS485总线是一种常见的串行总线标准,采用平衡发送与差分接收的方式,因此具有抑制共模干扰的能力.在一些要求通信距离为几十米到上千米的时候,RS485总线是一种应用最为广泛的 ...

  5. EasyDarwin开源流媒体服务器如何实现按需推送直播的

    --本文转自EasyDarwin开源团队成员邵帅的博客:http://blog.csdn.net/ss00_2012/article/details/51441753 我们使用EasyDarwin的推 ...

  6. [Phoenix] 二、数据类型

    目前Phoenix支持24种简单数据类型和1个一维Array的复杂类型.以下是对支持数据类型的说明: 1. INTEGER 2. UNSIGNED_INT 3. BIGINT 4. UNSIGNED_ ...

  7. Java NIO 粘包 拆包 (实战) - 史上最全解读

    疯狂创客圈 Java 聊天程序[ 亿级流量]实战系列之13 [博客园 总入口 ] 本文的源码工程:Netty 粘包/半包原理与拆包实战 源码 本实例是<Netty 粘包/半包原理与拆包实战> ...

  8. 对私有API提交的注意事项

    1.这个等于堵死了调试断点.关闭就不能断点调试了. 2.对于敏感的函数名要做一个对称加密处理. 防止二进制文件的静态扫描. 3.对于调用私有函数的方法,可以做一个宏定义包装. #define 你的正常 ...

  9. delphi2010\delphi XE7 开发及调试WebService 实例

    使用delphi已经10多年了,一直搞桌面程序开发,对Webservice一直很陌生,近来因工作需要,学习delphi开发WebService,担心遗忘,作此笔记. 特别感谢 中塑在线技术总监 大犇  ...

  10. Python pip源,解决安装慢的问题

    经常在使用Python的时候需要安装各种模块,而pip是很强大的模块安装工具,但是由于国外官方pypi经常被墙,导致不可用,所以我们最好是将自己使用的pip源更换一下,这样就能解决被墙导致的装不上库的 ...