题目链接 \(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. C-LODOP设置同一页面 手机电脑都打印

    C-Lodop有四种角色,1:客户端本地打印方式客户端访问web,调用客户端本地的打印机进行打印,这时候调用的安装在客户端本地的c-lodop服务,实际调用的是http://Localhost:800 ...

  2. How to recovery compiz

    sudo apt install compizconfig-settings-manager dconf reset -f /org/compiz/ setsid unity dconf list / ...

  3. codeforces722B

    Verse Pattern CodeForces - 722B You are given a text consisting of n lines. Each line contains some ...

  4. 微软已发布 Windows 10 Timeline 功能的官方 Chrome 插件

    微软已发布 Windows 10 Timeline 功能的官方 Chrome 插件,这个插件名为 Web Activities,功能是跨 Windows 10 和 Microsoft Launcher ...

  5. webpack——快速入门【一】

    学习webpack https://github.com/webproblem/learning-article#webpack https://github.com/lengziyu/learn-w ...

  6. 水课 or not

    很不幸,这学期的毛概老师是个老古董,讲的内容也甚是枯燥和迂腐,个人角度是不太喜欢.然而这也仅仅是站在个人感性的角度,唏嘘一下也就够了.听不下去了,写点东西. 有时候会想,是不是随着自己长大,渐渐地对专 ...

  7. mysql查询同一个字段下,不同内容的语句

    太久没有用SQL语句都有些忘记了,今天工作中遇到了那就尝试记录一下吧 需求是这样的:想查询同一个字段下,两条指定了不同内容,的其他的值 主要是要想到用where......in 语句如下:select ...

  8. 【BZOJ2095】【POI2010】Bridge 网络流

    题目大意 ​ 给你一个无向图,每条边的两个方向的边权可能不同.要求找出一条欧拉回路使得路径上的边权的最大值最小.无解输出"NIE". \(2\leq n\leq 1000,1\le ...

  9. Ionic生命周期与注意点

    需要注意的地方是:在走页面的生命周期以前,会先走构造方法 构造方法只走一次,除非再次创建这个页面.所以如果先push了一个新页面,然后再调用pop()返回到之前的页面,那么是不会走构造方法里面的方法的 ...

  10. Haproxy 配置 ACL 处理不同的 URL 请求

    需求说明服务器介绍:HAProxy Server: 192.168.1.90WEB1 : 192.168.1.103WEB2 : 192.168.1.105Domain: tecadmin.net当用 ...