BZOJ3881 Divljak
解:对被包含的那些串建AC自动机。
每次加一个串,就在AC自动机上面跑,可知能够跑到一些节点。
这些节点都是一些前缀的形式,我们跳fail树就是跳后缀,这样就能够得到所有能匹配的子串。
我们分别对AC自动机中的每个串计算答案。
就是要求一些节点到根路径的并集,并对其权值 + 1。查询就查对应节点。
然后有个trick就是lca处 - 1,节点处 + 1,然后求子树和。、
#include <bits/stdc++.h> const int N = , M = ; struct Edge {
int nex, v;
}edge[M]; int tp; int tr[M][], fail[M], tot = , n, ed[N];
std::queue<int> Q;
char str[N];
int e[M], pos[M], siz[M], num, ST[M << ][], d[M], stk[M], pw[M << ], top, pos2[M << ], num2; inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
} void DFS(int x) {
pos[x] = ++num;
pos2[x] = ++num2;
ST[num2][] = x;
siz[x] = ;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
d[y] = d[x] + ;
DFS(y);
ST[++num2][] = x;
siz[x] += siz[y];
}
return;
} inline void prework() {
for(int i = ; i <= num2; i++) {
pw[i] = pw[i >> ] + ;
}
for(int j = ; j <= pw[num2]; j++) {
for(int i = ; i + ( << j) - <= num2; i++) {
if(d[ST[i][j - ]] < d[ST[i + ( << (j - ))][j - ]])
ST[i][j] = ST[i][j - ];
else
ST[i][j] = ST[i + ( << (j - ))][j - ];
}
}
/*for(int j = 0; j <= pw[num2]; j++) {
printf("j = %d : ", j);
for(int i = 1; i + (1 << j) - 1 <= num2; i++) {
printf("%d ", ST[i][j]);
}
puts("");
}*/
return;
} inline bool cmp(const int &a, const int &b) {
return pos[a] < pos[b];
} inline int lca(int x, int y) {
//printf("lca : %d %d = ", x, y);
x = pos2[x]; y = pos2[y];
if(x > y) std::swap(x, y);
int t = pw[y - x + ];
if(d[ST[x][t]] < d[ST[y - ( << t) + ][t]])
return ST[x][t];
//{ printf("%d \n", ST[x][t]); return ST[x][t]; }
else
return ST[y - ( << t) + ][t];
//{ printf("%d \n", ST[y - (1 << t) + 1][t]); return ST[y - (1 << t) + 1][t]; }
} namespace ta {
int ta[M];
inline void add(int x, int v) {
for(int i = x; i <= tot; i += i & (-i)) {
ta[i] += v;
}
return;
}
inline int getSum(int x) {
int ans = ;
for(int i = x; i >= ; i -= i & (-i)) {
ans += ta[i];
}
return ans;
}
inline int ask(int l, int r) {
return getSum(r) - getSum(l - );
}
} inline void insert(int id) {
int n = strlen(str), p = ;
for(int i = ; i < n; i++) {
int f = str[i] - 'a';
if(!tr[p][f]) {
tr[p][f] = ++tot;
}
p = tr[p][f];
}
ed[id] = p;
return;
} inline void BFS() {
Q.push();
fail[] = ;
while(!Q.empty()) {
int x = Q.front();
Q.pop();
for(int f = ; f < ; f++) {
if(!tr[x][f]) continue;
int y = tr[x][f], j = fail[x];
while(j != && !tr[j][f]) {
j = fail[j];
}
if(x != && tr[j][f]) {
j = tr[j][f];
}
fail[y] = j;
Q.push(y);
}
}
return;
} inline void add() {
int n = strlen(str), p = ;
top = ;
for(int i = ; i < n; i++) {
int f = (str[i]) - 'a';
while(p != && !tr[p][f]) p = fail[p];
if(tr[p][f]) p = tr[p][f];
if(p > ) stk[++top] = p;
}
if(!top) return;
std::sort(stk + , stk + top + , cmp);
top = std::unique(stk + , stk + top + ) - stk - ;
for(int i = ; i <= top; i++) {
ta::add(pos[stk[i]], );
if(i < top) {
int z = lca(stk[i], stk[i + ]);
ta::add(pos[z], -);
}
}
return;
} inline int ask(int x) {
return ta::ask(pos[x], pos[x] + siz[x] - );
} int main() {
int m;
scanf("%d", &n);
for(int i = ; i <= n; i++) {
scanf("%s", str);
insert(i);
}
BFS();
///
for(int i = ; i <= tot; i++) {
add(fail[i], i);
//printf("add %d %d \n", fail[i], i);
}
d[] = ;
DFS();
prework(); scanf("%d", &m);
for(int i = , f, x; i <= m; i++) {
scanf("%d", &f);
if(f == ) {
scanf("%s", str);
add();
}
else {
scanf("%d", &x);
printf("%d\n", ask(ed[x]));
}
}
return ;
}
MLE代码
BZOJ3881 Divljak的更多相关文章
- fail树
前置技能:AC自动机 假设我们有了一个AC自动机,然后在上面进行字符串匹配. 上面是一个有四个字符串的AC自动机(abcde.aacdf.cdf.cde),虚线是fail指针,实线是转移. 这是上一次 ...
- Noip前的大抱佛脚----赛前任务
赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...
- 【BZOJ3881】[Coci2015]Divljak fail树+树链的并
[BZOJ3881][Coci2015]Divljak Description Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操 ...
- 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序
3881: [Coci2015]Divljak Time Limit: 20 Sec Memory Limit: 768 MBSubmit: 508 Solved: 158[Submit][Sta ...
- 【bzoj3881】【Coci2015】Divljak
题解 对$S$集合ac建自动机,把$T_{i}$放在里面跑,记录路径上的所有节点并对它们在fail树上求到root的树链并: 这样就得到了$T_{i}$所有的子串: 动态将$T_{i}$加入直接用树状 ...
- BZOJ3881 : [Coci2015]Divljak
对Alice的所有串构造AC自动机,并建出Fail树 每当Bob添加一个串时,在AC自动机上走,每走到一个点,就把它到根路径上所有点的答案+1 需要注意的是每次操作,相同的点只能被加一次 所以在需要操 ...
- BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并
题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...
- 【bzoj3881】[Coci2015]Divljak AC自动机+树链的并+DFS序+树状数组
题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...
- BZOJ3881 Coci2015 Divljak fail树+差分
题目大意,给出两个字符串集合S和T,向T中添加字符串,查询S_i在T中有几个字符串出现过.一看这种多字符串匹配问题,我们联想到了AC自动机,做法就是,对于S集合我们建立一个AC自动机,建出fail树, ...
随机推荐
- v-router几种定义方式
第一种 const router = new VueRouter({ routes: [{ path: '/newSongs', component: require('../views/NewSon ...
- 校园电商项目4——SSM各项配置
步骤一:数据库连接文件 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/school_o2o?useUni ...
- php7函数,声明,返回值等新特性介绍
使用 ... 运算符定义变长参数函数 (PHP 5 >= 5.6.0, PHP 7) 现在可以不依赖 func_get_args(), 使用 ... 运算符 来实现 变长参数函数. functi ...
- php redis常用方法代码例子
1,connect 描述:实例连接到一个Redis.参数:host: string,port: int返回值:BOOL 成功返回:TRUE;失败返回:FALSE 示例: <?php $redis ...
- mysql数据库的备份和还原的总结
mysql数据库的备份和还原的总结 (来自一运维同事的总结) 1. 备份方式: 热备:数据库在线进行备份,不影响读和写的在线备份方式! 温备:数据库在线进行备份,对表备份时先锁定写操作,仅可以执行读操 ...
- vs code軟件操作
https://www.imooc.com/article/39349 https://www.html.cn/archives/8144
- 解析xml文件 selectSingleNode取不到节点
今天在做批量生成XML的时候,碰到一个情况 解析xml文件 selectSingleNode一直返回NULL. XML的格式开头有一句这个<CE401Message xmlns="ht ...
- ES 6 系列 - 对于常用对象的拓展 api
本篇中学习并记录可能会比较常用的 api ,详细请自行查找相关资料. 一.字符串的拓展 es 6 加强了对于 Unicode 的支持.javascript 允许采用 \uxxxxx 的方式表示一个字符 ...
- Nginx proxy_protocol协议
L:113
- Kafka消费时报错:Producer connection to xxx:9092 unsuccessful
使用kafka消费数据时报Producer错误,具体错误如下: kafka.producer.SyncProducer:103 Producer connection to xxx:9092 unsu ...