题目链接 \(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]单词的更多相关文章

  1. 洛谷P3966 [TJOI2013]单词(fail树性质)

    P3966 [TJOI2013]单词 题目链接:https://www.luogu.org/problemnew/show/P3966 题目描述 小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单 ...

  2. P3966 [TJOI2013]单词

    P3966 [TJOI2013]单词 题目描述 小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次. 输入输出 ...

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

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

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

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

  5. [洛谷P3966][TJOI2013]单词

    题目大意:有$n$个字符串,求每个字符串在所有字符串中出现的次数 题解:$AC$自动机,每个节点被经过时$sz$加一,每一个字符串出现次数为其$fail$树子树$sz$和 卡点:$AC$自动机根节点为 ...

  6. 【洛谷】3966:[TJOI2013]单词【AC自动机】【fail树】

    P3966 [TJOI2013]单词 题目描述 小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次. 输入输出 ...

  7. BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status ...

  8. 【BZOJ3172】[Tjoi2013]单词 AC自动机

    [BZOJ3172][Tjoi2013]单词 Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input ...

  9. 3172: [Tjoi2013]单词

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3246  Solved: 1565[Submit][Status ...

随机推荐

  1. layui tips

  2. Lodop中特殊符号¥打印设计和预览不同

    Lodop中¥符号样式改变问题 Lodop中对超文本样式的解析,虽然说是按照调用的本机ie引擎,但是调用的ie版本可能不同,导致在ie下是一种样式,预览又是另一种样式.可能是有些样式没有具体设置,走的 ...

  3. sql中的begin catch 。。。end catch 的用法

    begin catch ...end  catch 是用来处理异常的 begintry--SQLendtry begincatch--sql (处理出错动作)endcatch 我们将可能会出错的sql ...

  4. poj-1236(强连通分量)

    题意:给你n个点,每个点可能有指向其他点的单向边,代表这个点可以把软件传给他指向的点,然后解决两个问题, 1.问你最少需要给几个点,才能使所有点都能拿到软件: 2.问你还需要增加几条单向边,才能使任意 ...

  5. 浅析Android设备中grep命令处理流程

    2017-04-18   概述     在TV开发板中,可以在串口中直接使用grep命令.这是因为在/system/bin/下有一个'grep'链接.这个链接指向'/system/bin/toolbo ...

  6. 51Nod1824 染色游戏 【Lucas定理】【FMT】【位运算】

    我的FMT是在VFleaKing的论文中学到的.51Nod的评测机好恶心. 题目分析: 题目很明显是要你求一个类似卷积的式子.但是我们可以注意到前面具有组合数,如果拆成阶乘会很大,在模意义下你无法判断 ...

  7. wstngfw openVpn站点到站点连接示例(SSL/TLS)

    wstngfw openVpn站点到站点连接示例(SSL/TLS) 在本例中,将假设以下设置: 站点 A 站点 B 名称 Beijing Office(北京办公室) 名称 Shenzheng Offi ...

  8. C# 成员默认访问权限(public、private、protected、internal)

    C# 成员默认访问权限(public.private.protected.internal) 来源 https://www.cnblogs.com/yezongjie/p/20181121Access ...

  9. expect 自动化控制命令

    expect 的核心是 spawn expect send set spawn 调用要执行的命令expect 等待命令提示信息的出现,也就是捕捉用户输入的提示:send 发送需要交互的值,替代了用户手 ...

  10. Hdoj 1421.搬寝室 题解

    Problem Description 搬寝室是很累的,xhd深有体会.时间追述2006年7月9号,那天xhd迫于无奈要从27号楼搬到3号楼,因为10号要封楼了.看着寝室里的n件物品,xhd开始发呆, ...