[COCI2015]Divljak
题目大意
有 个字符串 和一个字符串集合 ,一开始 为空。
然后有两个操作:
1 P
往 里加一个字符串 。2 x
询问集合 中有多少个字符串包含串 (我们称串 包含串 ,当且仅当 是 的子串)。
对于 的数据,,字符串由小写字母构成,所有字符串的总长 。
解题思路
显然,看到数据范围可以想到肯定不能暴力。
首先考虑对 建 AC
自动机。
然后就没思路了。。。
但是,往往模拟一遍就会有思路了。
于是,模拟暴力时是怎样对答案进行求和的。
显然,做过模板题的都知道,将串放在 AC
自动机上一位一位跑,每跑一位将 fail
链上的所有贡献加起来,等等,这是不是似曾相识??
考虑建 fail
树,fail
链即是一个点到根节点的简单路径。
现在就要用到树链剖分了。
那树上差分的身影又在哪呢?
可以想到,对串的每一位都求路径和,肯定会炸。
考虑优化,将串的每一位根据 dfn
序进行排序。
然后,将每个点贡献加 ,相邻两点的 lca
贡献减 。
树上差分就有了。
对于每个询问,对串在 trie
上结束的节点求一遍子树和就行了。
AC CODE
#include <bits/stdc++.h>
using namespace std;
const int _ = 2e6 + 7;
int n, m;
int cnt = 1, tr[_][27], fail[_], tag[_];
char c[_];
void insert(char *s, int id)
{
int p = 1;
for(int i = 1, len = strlen(s + 1); i <= len; ++i)
{
int v = s[i] - 'a';
if(!tr[p][v]) tr[p][v] = ++cnt;
p = tr[p][v];
}
tag[id] = p;
}
void getfail()
{
for (int i = 0; i < 26; i ++)
tr[0][i] = 1;
queue<int> q;
q.push(1);
fail[1] = 0;
while(!q.empty())
{
int now = q.front();
q.pop();
for(int i = 0; i < 26; ++i)
{
if(tr[now][i])
{
q.push(tr[now][i]);
fail[tr[now][i]] = tr[fail[now]][i];
}
else
{
tr[now][i] = tr[fail[now]][i];
}
}
}
}
int tot, head[_], to[_ << 1], nxt[_ << 1];
void add(int u, int v)
{
to[++tot] = v;
nxt[tot] = head[u];
head[u] = tot;
}
int cnt_node, dep[_], siz[_], hson[_], top[_], fa[_], dfn[_];
void dfs1(int u, int d = 1)
{
dep[u] = d;
siz[u] = 1;
for(int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if(dep[v]) continue;
fa[v] = u;
dfs1(v, d + 1);
siz[u] += siz[v];
if(siz[v] > siz[hson[u]]) hson[u] = v;
}
}
void dfs2(int u, int topf)
{
dfn[u] = ++cnt_node;
top[u] = topf;
if(!hson[u]) return;
dfs2(hson[u], topf);
for(int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if(top[v]) continue;
dfs2(v, v);
}
}
int LCA(int u, int v)
{
while(top[u] != top[v])
{
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] > dep[v] ? v : u;
}
int k[_];
int lowbit(int x)
{
return x & -x;
}
void update(int x, int val)
{
for(int i = x; i <= cnt; i += lowbit(i))
{
k[i] += val;
}
}
int query(int x)
{
int res = 0;
for(int i = x; i; i -= lowbit(i))
{
res += k[i];
}
return res;
}
bool cmp(int x, int y)
{
return dfn[x] < dfn[y];
}
int q[_];
signed main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%s", c + 1);
insert(c, i);
}
getfail();
for(int i = 2; i <= cnt; ++i)
add(fail[i], i);
dfs1(1);
dfs2(1, 1);
scanf("%d", &m);
for(int i = 1; i <= m; ++i)
{
int op, kkk;
scanf("%d", &op);
if(op == 1)
{
scanf("%s", c + 1);
int len = strlen(c + 1);
for(int j = 1, p = 1; j <= len; ++j)
{
int v = c[j] - 'a';
p = tr[p][v];
q[j] = p;
}
sort(q + 1, q + len + 1, cmp);
for(int j = 1; j <= len; ++j)
update(dfn[q[j]], 1);
for(int j = 1; j < len; ++j)
update(dfn[LCA(q[j], q[j + 1])], -1);
}
else
{
scanf("%d", &kkk);
int x = tag[kkk];
printf("%d\n", query(dfn[x] + siz[x] - 1) - query(dfn[x] - 1));
}
}
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122118
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话