题面

Description

Tom有n个字符串S1,S2...Sn,Jerry有一个字符串集合T,一开始集合是空的。

接下来会发生q个操作,操作有两种形式:

“1 P”,Jerry往自己的集合里添加了一个字符串P。

“2 x”,Tom询问Jerry,集合T中有多少个字符串包含串Sx。(我们称串A包含串B,当且仅当B是A的子串)

Jerry遇到了困难,需要你的帮助。

Input

第1行,一个数n;

接下来n行,每行一个字符串表示Si;

下一行,一个数q;

接下来q行,每行一个操作,格式见题目描述。

Output

对于每一个Tom的询问,帮Jerry输出答案。

Sample Input

3
a
bc
abc
5
1 abca
2 1
1 bca
2 2
2 3

Sample Output

1
2
1

HINT

100%: \(1≤N,Q≤100000\), \(\sum_{s \in S} |s|, \sum_{s \in T}|s| \le 2 \times 10^6\), \(alphabet = \{a ... z\}\)

Solution

正解貌似是fail树加树链的并, 但我写的是后缀树. 还没读入完空间就炸了... 先放一份代码吧

#include <cstdio>
#include <cstring>
#include <vector>
#define vector std::vector const int LEN = (int)2e6, N = (int)1e5, Q = (int)1e5;
int q;
int ans[Q];
struct segmentTree
{
struct node
{
node *suc[2];
int sz;
inline node() {for(int i = 0; i < 2; ++ i) suc[i] = NULL; sz = 0;}
}*rt;
inline segmentTree() {rt = NULL;}
node* insert(node *u, int L, int R, int pos)
{
if(u == NULL) u = new node;
if(L == R) {u->sz = 1; return u;}
if(pos <= L + R >> 1) u->suc[0] = insert(u->suc[0], L, L + R >> 1, pos); else u->suc[1] = insert(u->suc[1], (L + R >> 1) + 1, R, pos);
u->sz = 0;
for(int i = 0; i < 2; ++ i) if(u->suc[i] != NULL) u->sz += u->suc[i]->sz;
return u;
}
inline void insert(int pos) {rt = insert(rt, 1, q, pos);}
int query(node *u, int L, int R, int pos)
{
if(u == NULL) return 0;
if(R <= pos) return u->sz;
return query(u->suc[0], L, L + R >> 1, pos) + (pos > L + R >> 1 ? query(u->suc[1], (L + R >> 1) + 1, R, pos) : 0);
}
inline int query(int pos) {return query(rt, 1, q, pos);}
node* merge(int L, int R, node *u, node *_u)
{
if(u == NULL) return _u; if(_u == NULL) return u;
if(L == R) {u->sz |= _u->sz; delete _u; return u;}
u->suc[0] = merge(L, L + R >> 1, u->suc[0], _u->suc[0]); u->suc[1] = merge((L + R >> 1) + 1, R, u->suc[1], _u->suc[1]);
delete _u;
u->sz = 0;
for(int i = 0; i < 2; ++ i) if(u->suc[i] != NULL) u->sz += u->suc[i]->sz;
return u;
}
inline void merge(segmentTree *T) {rt = merge(1, q, rt, T->rt);}
};
struct suffixAutomaton
{
struct node
{
node *suc[26], *pre;
int len;
vector<int> bck, qry;
int vst;
vector<node*> successorOnSuffixTree;
inline node() {for(int i = 0; i < 26; ++ i) suc[i] = NULL; pre = NULL; vst = 0; bck.clear(); qry.clear(); successorOnSuffixTree.clear();}
}*rt;
inline suffixAutomaton() {rt = new node; rt->len = 0;}
inline node* insert(char *str, int len, int id)
{
node *lst = rt;
for(int i = 0; i < len; ++ i)
{
int c = str[i] - 'a';
if(lst->suc[c] != NULL)
{
node *p = lst->suc[c];
if(p->len == lst->len + 1) { if(~ id) p->bck.push_back(id); lst = p; continue;}
node *q = new node; if(~ id) q->bck.push_back(id);
for(int i = 0; i < 26; ++ i) q->suc[i] = p->suc[i]; q->pre = p->pre; q->len = lst->len + 1;
p->pre = q;
for(; lst != NULL && lst->suc[c] == p; lst = lst->pre) lst->suc[c] = q;
lst = q;
continue;
}
node *u = new node; u->len = lst->len + 1; if(~ id) u->bck.push_back(id);
for(; lst != NULL && lst->suc[c] == NULL; lst = lst->pre) lst->suc[c] = u;
if(lst == NULL) u->pre = rt;
else
{
node *p = lst->suc[c];
if(p->len == lst->len + 1) u->pre = p;
else
{
node *q = new node; for(int i = 0; i < 26; ++ i) q->suc[i] = p->suc[i]; q->pre = p->pre; q->len = lst->len + 1;
u->pre = p->pre = q;
for(; lst != NULL && lst->suc[c] == p; lst = lst->pre) lst->suc[c] = q;
}
}
lst = u;
}
return lst;
}
void build(node *u)
{
u->vst = 1; if(u->pre != NULL) u->pre->successorOnSuffixTree.push_back(u);
for(int i = 0; i < 26; ++ i) if(u->suc[i] != NULL && ! u->suc[i]->vst) build(u->suc[i]);
}
inline void buildSuffixTree() {build(rt);}
segmentTree* DFS(node *u)
{
segmentTree *seg = new segmentTree;
for(auto id : u->bck) seg->insert(id);
for(auto v : u->successorOnSuffixTree)
{
segmentTree *res = DFS(v);
seg->merge(res);
}
for(auto qry : u->qry) ans[qry] = seg->query(qry);
return seg;
}
inline void work() {DFS(rt);}
}SAM;
int main()
{ #ifndef ONLINE_JUDGE freopen("davljak.in", "r", stdin);
freopen("davljak.out", "w", stdout); #endif int n; scanf("%d\n", &n);
static char str[LEN];
static suffixAutomaton::node *ed[N + 1];
for(int i = 1; i <= n; ++ i) scanf("%s", str), ed[i] = SAM.insert(str, strlen(str), -1);
scanf("%d", &q);
for(int i = 1; i <= q; ++ i)
{
int opt; scanf("%d ", &opt);
if(opt == 1) scanf("%s", str), SAM.insert(str, strlen(str), i);
else if(opt == 2)
{
int x; scanf("%d", &x);
ed[x]->qry.push_back(i);
}
}
SAM.buildSuffixTree();
memset(ans, -1, sizeof(ans));
SAM.work();
for(int i = 1; i <= q; ++ i) if(~ ans[i]) printf("%d\n", ans[i]);
}

BZOJ 3881 COCI 2015 Divljak的更多相关文章

  1. [BZOJ 3745] [COCI 2015] Norma

    Description 给定一个正整数序列 \(a_1,a_2,\cdots,a_n\),求 \[ \sum_{i=1}^n\sum_{j=i}^n(j-i+1)\min(a_i,a_{i+1},\c ...

  2. bzoj 3743 [ Coci 2015 ] Kamp —— 树形DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3743 一开始想到了树形DP,处理一下子树中的最小值,向上的最小值,以及子树中的最长路和向上的 ...

  3. [LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机

    [LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机 题意 给定平面上的 \(n\) 个整点 \((x_i,y_i)\), 一共有两个问题. 第一个问题是从原 ...

  4. [LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会

    [LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会 题意 给定一个长度为 \(n\) 的字符串 \(s\), 对于所有 \(r\in[1,n]\) 求出 \(s\ ...

  5. [BZOJ 3992] [SDOI 2015] 序列统计(DP+原根+NTT)

    [BZOJ 3992] [SDOI 2015] 序列统计(DP+原根+NTT) 题面 小C有一个集合S,里面的元素都是小于质数M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数 ...

  6. [BZOJ 3930] [CQOI 2015]选数(莫比乌斯反演+杜教筛)

    [BZOJ 3930] [CQOI 2015]选数(莫比乌斯反演+杜教筛) 题面 我们知道,从区间\([L,R]\)(L和R为整数)中选取N个整数,总共有\((R-L+1)^N\)种方案.求最大公约数 ...

  7. 【BZOJ 3881】【COCI 2015】Divljak

    http://www.lydsy.com/JudgeOnline/problem.php?id=3881 好难的一道题啊qwq 一开始我想对T建AC自动机,根本不可做. 正解是对S建AC自动机. fa ...

  8. BZOJ 3881: [Coci2015]Divljak [AC自动机 树链的并]

    3881: [Coci2015]Divljak 题意:添加新文本串,询问某个模式串在多少种文本串里出现过 模式串建AC自动机,考虑添加一个文本串,走到的节点记录下来求树链的并 方法是按dfs序排序去重 ...

  9. BZOJ 3881: [Coci2015]Divljak

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

随机推荐

  1. 使用chrome开发程序,自动刷新开发目录

    npm i livereload -g 在开发目录下: livereload 安装这个插件:https://chrome.google.com/webstore/detail/livereload/j ...

  2. XmlSerializer 短信备份

    package com.itheima.mobileguard.utils; import java.io.File; import java.io.FileNotFoundException; im ...

  3. C#环境下使用Windows消息传递字符串数据的研究

    前言: 日前,师兄交给了我一个课题,将一个后台运行的Console程序,修改为WindowsForm程序,并在其界面上增加配置设置,以及运行记录。 原来的Console程序,后台运行的流程在其中一个类 ...

  4. bugku 普通的二维码

    记录下对进制转换实现的重新思考. 扫描二维码扫到了一句垃圾话. 拖到winhex里面. 一开始以为是十进制直接转ascii,发现错误. 后来发现,最大数是7,八进制转换吧. 我是打算用python的l ...

  5. Backpropagation Through Time (BPTT) 梯度消失与梯度爆炸

    Backpropagation Through Time (BPTT) 梯度消失与梯度爆炸 下面的图显示的是RNN的结果以及数据前向流动方向 假设有 \[ \begin{split} h_t & ...

  6. [oldboy-django][2深入django]学生管理(Form)-- 添加(美化Form表单:通过form给前端标签添加属性)

    1 在student_list添加一个a标签, <p><a href="/app01/add_student" class="btn btn-prima ...

  7. c++中读取文件最快的方法

    https://www.byvoid.com/blog/fast-readfile 可以看看了.

  8. JMeter 性能测试进阶实战

    课程简介 本课程制作的主要目的是为了让大家快速上手 JMeter,期间穿插了大量主流项目中用到的技术,以及结合当今主流微服务技术提供了测试 Dubbo 接口.Java 工程技术具体实施方案,注重实践. ...

  9. SolrCloud在linux上的搭建

    SolrCloud在linux上的搭建 1.环境准备 三台虚拟机的环境准备: 1. 更改主机名 2. 关闭selinux 3. 关闭防火墙 4. 更改主机名与ip地址的映射 5. 时钟同步 6. ss ...

  10. BZOJ3571 [Hnoi2014]画框 【分治 + KM算法】

    题目链接 BZOJ3571 题解 如果知道最小乘积生成树,那么这种双权值乘积最小就是裸题了 将两权值和作为坐标,转化为二维坐标系下凸包上的点,然后不断划分分治就好了 这里求的是最小匹配值,每次找点套一 ...