解:对被包含的那些串建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的更多相关文章

  1. fail树

    前置技能:AC自动机 假设我们有了一个AC自动机,然后在上面进行字符串匹配. 上面是一个有四个字符串的AC自动机(abcde.aacdf.cdf.cde),虚线是fail指针,实线是转移. 这是上一次 ...

  2. Noip前的大抱佛脚----赛前任务

    赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...

  3. 【BZOJ3881】[Coci2015]Divljak fail树+树链的并

    [BZOJ3881][Coci2015]Divljak Description Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操 ...

  4. 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序

    3881: [Coci2015]Divljak Time Limit: 20 Sec  Memory Limit: 768 MBSubmit: 508  Solved: 158[Submit][Sta ...

  5. 【bzoj3881】【Coci2015】Divljak

    题解 对$S$集合ac建自动机,把$T_{i}$放在里面跑,记录路径上的所有节点并对它们在fail树上求到root的树链并: 这样就得到了$T_{i}$所有的子串: 动态将$T_{i}$加入直接用树状 ...

  6. BZOJ3881 : [Coci2015]Divljak

    对Alice的所有串构造AC自动机,并建出Fail树 每当Bob添加一个串时,在AC自动机上走,每走到一个点,就把它到根路径上所有点的答案+1 需要注意的是每次操作,相同的点只能被加一次 所以在需要操 ...

  7. BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并

    题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

  8. 【bzoj3881】[Coci2015]Divljak AC自动机+树链的并+DFS序+树状数组

    题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

  9. BZOJ3881 Coci2015 Divljak fail树+差分

    题目大意,给出两个字符串集合S和T,向T中添加字符串,查询S_i在T中有几个字符串出现过.一看这种多字符串匹配问题,我们联想到了AC自动机,做法就是,对于S集合我们建立一个AC自动机,建出fail树, ...

随机推荐

  1. v-router几种定义方式

    第一种 const router = new VueRouter({ routes: [{ path: '/newSongs', component: require('../views/NewSon ...

  2. 校园电商项目4——SSM各项配置

    步骤一:数据库连接文件 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/school_o2o?useUni ...

  3. php7函数,声明,返回值等新特性介绍

    使用 ... 运算符定义变长参数函数 (PHP 5 >= 5.6.0, PHP 7) 现在可以不依赖 func_get_args(), 使用 ... 运算符 来实现 变长参数函数. functi ...

  4. php redis常用方法代码例子

    1,connect 描述:实例连接到一个Redis.参数:host: string,port: int返回值:BOOL 成功返回:TRUE;失败返回:FALSE 示例: <?php $redis ...

  5. mysql数据库的备份和还原的总结

    mysql数据库的备份和还原的总结 (来自一运维同事的总结) 1. 备份方式: 热备:数据库在线进行备份,不影响读和写的在线备份方式! 温备:数据库在线进行备份,对表备份时先锁定写操作,仅可以执行读操 ...

  6. vs code軟件操作

    https://www.imooc.com/article/39349 https://www.html.cn/archives/8144

  7. 解析xml文件 selectSingleNode取不到节点

    今天在做批量生成XML的时候,碰到一个情况 解析xml文件 selectSingleNode一直返回NULL. XML的格式开头有一句这个<CE401Message xmlns="ht ...

  8. ES 6 系列 - 对于常用对象的拓展 api

    本篇中学习并记录可能会比较常用的 api ,详细请自行查找相关资料. 一.字符串的拓展 es 6 加强了对于 Unicode 的支持.javascript 允许采用 \uxxxxx 的方式表示一个字符 ...

  9. Nginx proxy_protocol协议

    L:113

  10. Kafka消费时报错:Producer connection to xxx:9092 unsuccessful

    使用kafka消费数据时报Producer错误,具体错误如下: kafka.producer.SyncProducer:103 Producer connection to xxx:9092 unsu ...