Luogu P3966 [TJOI2013]单词
题目链接 \(Click\) \(Here\)
本题\(AC\)自动机写法的正解之一是\(Fail\)树上跑\(DP\)。
\(AC\)自动机是\(Trie\)树和\(Fail\)树共存的结构,前者可以方便地处理前缀问题,而在后者中,一个节点的子节点,代表以当前字符串为后缀的所有字符串节点(根节点外向\(Fail\)树)。我们最初给每个串的所有前缀计数\(+1\),后期统计时,在该前缀的所有后缀上(\(Fail\)树上的祖先节点上),将其自身答案累加上去,就是总共出现的次数。
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
struct AC_Auto {
int top, sta[N];
long long sum[N];
int cnt, ch[N][26], pre[N], fail[N];
AC_Auto () {
cnt = top = 0;
memset (ch, 0, sizeof (ch));
memset (sum, 0, sizeof (sum));
memset (pre, 0, sizeof (pre));
memset (fail, 0, sizeof (fail));
}
void add_str (char *s) {
int l = strlen (s), now = 0;
for (int i = 0; i < l; ++i) {
if (!ch[now][s[i] - 'a']) {
ch[now][s[i] - 'a'] = ++cnt;
}
pre[ch[now][s[i] - 'a']] = now;
now = ch[now][s[i] - 'a'];
sum[now]++;
}
sta[++top] = now;
}
queue <int> q;
void build () {
for (int i = 0; i < 26; ++i) {
if (ch[0][i]) {
q.push (ch[0][i]);
}
}
while (!q.empty ()) {
int u = q.front (); q.pop ();
for (int i = 0; i < 26; ++i) {
if (ch[u][i]) {
q.push (ch[u][i]);
fail[ch[u][i]] = ch[fail[u]][i];
} else {
ch[u][i] = ch[fail[u]][i];
}
}
}
}
int _cnt, head[N];
struct edge {
int nxt, to;
}e[N];
void add_edge (int from, int to) {
e[++_cnt].nxt = head[from];
e[_cnt].to = to;
head[from] = _cnt;
}
void dp (int u) {
for (int i = head[u]; ~i; i = e[i].nxt) {
dp (e[i].to);
sum[u] += sum[e[i].to];
}
}
void get_ans () {
_cnt = 0;
memset (head, -1, sizeof (head));
for (int i = 1; i <= cnt; ++i) {
add_edge (fail[i], i);
}
dp (0);
for (int i = 1; i <= top; ++i) {
cout << sum[sta[i]] << endl;
}
}
}AC;
int n; char s[N];
int main () {
cin >> n;
for (int i = 1; i <= n; ++i) {
scanf ("%s", s);
AC.add_str (s);
}
AC.build ();
AC.get_ans ();
}
\(UPD:\)新增了后缀数组\(+ST\)表\(+\)倍增写法
#include <bits/stdc++.h>
using namespace std;
const int N = 2000010;
char tmp[N];
int n, len, ban = 'z', s[N], id[N], sa[N], tp[N], rk[N], _rk[N], bin[N], _len[N], height[N];
void get_height (int n) {
int k = 0;
for (int i = 1; i <= n; ++i) {
if (k != 0) --k;
int j = sa[rk[i] - 1];
while (s[i + k] == s[j + k]) ++k;
height[rk[i]] = k;
}
}
void base_sort (int n, int m) {
for (int i = 0; i <= m; ++i) bin[i] = 0;
for (int i = 1; i <= n; ++i) bin[rk[tp[i]]]++;
for (int i = 1; i <= m; ++i) bin[i] += bin[i - 1];
for (int i = n; i >= 1; --i) sa[bin[rk[tp[i]]]--] = tp[i];
}
void suffix_sort (int n, int m) {
for (int i = 1; i <= n; ++i) {
tp[i] = i, rk[i] = s[i];
}
base_sort (n, m);
for (int w = 1; w <= n; w <<= 1) {
int cnt = 0;
for (int i = n - w + 1; i <= n; ++i) tp[++cnt] = i;
for (int i = 1; i <= n; ++i) if (sa[i] > w) tp[++cnt] = sa[i] - w;
base_sort (n, m);
memcpy (_rk, rk, sizeof (rk));
rk[sa[1]] = cnt = 1;
for (int i = 2; i <= n; ++i) {
rk[sa[i]] = (_rk[sa[i - 1]] == _rk[sa[i]]) && (_rk[sa[i - 1] + w] == _rk[sa[i] + w]) ? cnt : ++cnt;
}
if (cnt == n) break;
m = cnt;
}
}
int fa[N][25];
int query (int l, int r) {
if (l >= r) return 0; l++;
int mx = log2 (r - l + 1);
return min (fa[l][mx], fa[r - (1 << mx) + 1][mx]);
}
void get_STlist (int n) {
int mx = log2 (n);
for (int i = 1; i <= n; ++i) fa[i][0] = height[i];
for (int i = 1; i <= mx; ++i) {
for (int j = 1; j <= n - (1 << i) + 1; ++j) {
fa[j][i] = min (fa[j][i - 1], fa[j + (1 << (i - 1))][i - 1]);
}
}
}
int main () {
cin >> n;
for (int i = 1; i <= n; ++i) {
scanf ("%s", tmp);
int l = strlen (tmp);
id[i] = len + 1;
_len[i] = l;
for (int j = 0; j < l; ++j) {
s[++len] = tmp[j];
}
s[++len] = ++ban;
}
suffix_sort (len, ban);
get_height (len);
get_STlist (len);
for (int i = 1; i <= n; ++i) {
int l = rk[id[i]], r = rk[id[i]];
for (int j = 24; j >= 0; --j) {
int tol = l - (1 << j);
int tor = r + (1 << j);
if (tol >= 1 && query (tol, rk[id[i]]) >= _len[i]) l = tol;
if (tor <= len && query (rk[id[i]], tor) >= _len[i]) r = tor;
}
printf ("%d\n", r - l + 1);
}
}
Luogu P3966 [TJOI2013]单词的更多相关文章
- 洛谷P3966 [TJOI2013]单词(fail树性质)
P3966 [TJOI2013]单词 题目链接:https://www.luogu.org/problemnew/show/P3966 题目描述 小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单 ...
- P3966 [TJOI2013]单词
P3966 [TJOI2013]单词 题目描述 小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次. 输入输出 ...
- 洛谷P3966 [TJOI2013]单词(AC自动机)
题目描述 小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次. 输入输出格式 输入格式: 第一行一个整数N,表 ...
- 洛谷P3966 [TJOI2013]单词(后缀自动机)
传送门 统计单词出现次数……为啥大家都是写AC自动机的嘞……明明后缀自动机也能做的说…… 统计出现次数这个就直接按长度排序然后做个dp就好,这是SAM的板子的要求啊,不提了 然后考虑怎么让所有串之间隔 ...
- [洛谷P3966][TJOI2013]单词
题目大意:有$n$个字符串,求每个字符串在所有字符串中出现的次数 题解:$AC$自动机,每个节点被经过时$sz$加一,每一个字符串出现次数为其$fail$树子树$sz$和 卡点:$AC$自动机根节点为 ...
- 【洛谷】3966:[TJOI2013]单词【AC自动机】【fail树】
P3966 [TJOI2013]单词 题目描述 小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次. 输入输出 ...
- BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]
3172: [Tjoi2013]单词 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3198 Solved: 1532[Submit][Status ...
- 【BZOJ3172】[Tjoi2013]单词 AC自动机
[BZOJ3172][Tjoi2013]单词 Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input ...
- 3172: [Tjoi2013]单词
3172: [Tjoi2013]单词 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3246 Solved: 1565[Submit][Status ...
随机推荐
- Python实现快速排序--数据结构
快速排序(Quick Sort) 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序n个元素要O(nlogn)次比较.在最坏状况下则需要O(n^2)次比较,但这种状况并不常见.事实上,快速 ...
- Spring Boot 构建电商基础秒杀项目 (三) 通用的返回对象 & 异常处理
SpringBoot构建电商基础秒杀项目 学习笔记 定义通用的返回对象 public class CommonReturnType { // success, fail private String ...
- vhdl——type
TYPE 数据类型名 IS 数据类型定义 OF 基本数据类型 TYPE 数据类型名 IS 数据类型定义 常用的用户自定义的数据类型有枚举型,数组型,记录型.其中枚举型的在状态机的描述中经常使用到 ,数 ...
- JarvisOJ BASIC -.-字符串
请选手观察以下密文并转换成flag形式 ..-. .-.. .- --. ..... ..--- ..--- ----- .---- ---.. -.. -.... -.... ..... ...-- ...
- js判断一个元素是否在数组中
js判断一个元素是否在数组中 var arr = ['a','s','d','f']; console.info(isInArray(arr,'a'));//循环的方式 function isInAr ...
- PIGS POJ - 1149(水最大流)
题意: 有M个猪圈,每个猪圈里初始时有若干头猪.一开始所有猪圈都是关闭的.依次来了N个顾客,每个顾客分别会打开指定的几个猪圈,从中买若干头猪.每个顾客分别都有他能够买的数量的上限.每个顾客走后,他打开 ...
- 【XSY2741】网格 分治 LCT 并查集
题目描述 有一个\(n\times m\)的网格,线框的交点可以扭动,边不可伸缩.网格中有一些格子里面放了'x'形的支架,这些格子不会变形,但可以整体转动.如果所有格子都不能变形,那么称这个网格稳固. ...
- 【AGC006C】Rabbit Exercise 置换
题目描述 有\(n\)只兔子站在数轴上.为了方便,将这些兔子标号为\(1\ldots n\).第\(i\)只兔子的初始位置为\(a_i\). 现在这些兔子会按照下面的规则做若干套体操.每一套体操由\( ...
- PhoneGap & Cordova 安装白皮书
1.前题: PhoneGap是一个用基于HTML,CSS和JavaScript的,创建移动跨平台移动应用程序的快速开发平台.它使开发者能够利用 iPhone,Android,Palm,Symbian, ...
- 记OI退役
前言 (这篇本来在联赛前写了一点,但是一直没有发布.现在退役了,还是把它发出来留作纪念吧!) 其实,这篇随笔早该在停课时就写,可是我却迟迟没有动笔. 可能是我真的太懒了,或许也是我想要逃避自己内心的真 ...