Luogu 2414 [NOI 2011] 阿狸的打字机

2023-05-16

文章目录

          • 传送门
          • 思路
          • 参考代码
          • 总结

传送门
思路

首先我们甚至不能单独保存每个字符串,因为总长度可以达到 O ( n 2 ) O(n^2) O(n2)。于是我们考虑 Trie 树:显然 Trie 树的结点总数是 O ( n ) O(n) O(n) 的。但是建立 Trie 树时显然也不能暴力建树,我们跟随打字机的脚步就可以了,虽然这样会有多的结点,但是总结点数是 O ( n ) O(n) O(n) 的。

现在要询问串 x x x 在串 y y y 中出现了多少次,我们自然会想到把串 y y y 放在 AC 自动机上跑,匹配到 x x x 的次数就是答案。这样我们立即得到一个时间复杂度为 O ( n m ) O(nm) O(nm) 的算法。

我们考虑 AC 自动机的后缀链接树(注:fail 树)。显然,我们看匹配到 x x x 的次数的方法是检查从当前结点出发沿着后缀链接树向上走的所有结点有多少 x x x。由于 y y y 也是在 AC 自动机里的,因此我们要求的就是某条链上所有结点能够到 x x x 的个数之和。

我们考虑倒着求:不求某条链上所有结点出发能够到达的 x x x 的个数之和,而是求从 x x x 出发能够到这条链上多少个结点。在 Trie 树上,我们给当前链上的结点打标记;在后缀链接树上,我们统计 x x x 的子树中有多少个有标记的:这个显然可以用树状数组 + DFS 序来完成。

考虑离线,将询问按第一个参数排序,然后照着给出的指令走一次。走到一个结点上时,我们给这个结点打上标记;离开时删除标记。对于某个地方的询问,由于我们的链上是处理好了的,所以我们直接在树状数组里查询第二个参数对应结点在后缀链接树上有多少个打了标记的结点。这样,我们就能在 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的时间复杂度内解决这个问题了。

参考代码

注意各编号的对应关系

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef int INT_PUT;
INT_PUT readIn()
{
	INT_PUT a = 0; bool positive = true;
	char ch = getchar();
	while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
	if (ch == '-') { positive = false; ch = getchar(); }
	while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
	return positive ? -a : a;
}
void printOut(INT_PUT x)
{
	char buffer[20]; int length = 0;
	if (x < 0) putchar('-'); else x = -x;
	do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
	do putchar(buffer[--length]); while (length);
	putchar('\n');
}

const int maxn = int(1e5) + 5;
char origin[maxn];
int n, m, q;
int querys[maxn][2];
int pos[maxn];

int idx[maxn];
bool comp(const int& a, const int& b)
{
	return querys[a][1] < querys[b][1];
}
int ans[maxn];

struct Graph
{
	struct Edge
	{
		int to;
		int next;
	} edges[maxn];
	int i;
	int head[maxn];
	Graph() : i() { memset(head, -1, sizeof(head)); }
	void addEdge(int from, int to)
	{
		edges[i].to = to;
		edges[i].next = head[from];
		head[from] = i;
		i++;
	}
#define idx(G) idx_##G
#define wander(G, node) for (int idx(G) = G.head[node]; ~idx(G); idx(G) = G.edges[idx(G)].next)
#define DEF(G) const Graph::Edge& e = G.edges[idx(G)]; int to = e.to
};
struct BIT
{
	int bit[maxn];
	static inline int lowbit(int x) { return x & -x; }
	BIT() : bit() {}

	void add(int pos, int val, int size)
	{
		while (pos <= size)
		{
			bit[pos] += val;
			pos += lowbit(pos);
		}
	}
	int query(int pos)
	{
		int ret = 0;
		while (pos)
		{
			ret += bit[pos];
			pos ^= lowbit(pos);
		}
		return ret;
	}
	int query(int l, int r)
	{
		return query(r) - query(l - 1);
	}
};

struct ACAutomaton
{
	static const int alphabet = 26;
	static inline int code(char ch)
	{
		return ch - 'a';
	}
	struct Node
	{
		int parent;
		int ch[alphabet];
		int fail;
		int suffix;
		bool at;
		Node() {}
	} nodes[maxn];
	int size;

	void build()
	{
		size = 1;
		int cnt = 0;
		for (char* ch = origin; *ch; ch++)
		{
			if (*ch == 'B')
				cnt = nodes[cnt].parent;
			else if (*ch == 'P')
			{
				nodes[cnt].at = true;
				pos[++n] = cnt;
			}
			else
			{
				int x = code(*ch);
				if (!nodes[cnt].ch[x])
				{
					nodes[cnt].ch[x] = size;
					nodes[size++].parent = cnt;
				}
				cnt = nodes[cnt].ch[x];
			}
		}
	}
	void initACAutomaton()
	{
		static int queue[maxn];
		int head = 0, tail = 0;
		queue[tail++] = 0;
		while (head != tail)
		{
			int from = queue[head++];
			for (int i = 0; i < alphabet; i++)
			{
				if (int t = nodes[from].ch[i])
				{
					int temp = nodes[from].fail;
					while (temp && !nodes[temp].ch[i])
						temp = nodes[temp].fail;
					nodes[t].fail = temp = from ? nodes[temp].ch[i] : 0;
					nodes[t].suffix = nodes[temp].at ? temp : nodes[temp].suffix;
					queue[tail++] = t;
				}
			}
		}
	}

	Graph G;
	void buildGraph()
	{
		for (int i = 1; i < size; i++)
			G.addEdge(nodes[i].suffix, i);
	}

	int stamp;
	void DFS()
	{
		stamp = 0;
		DFS(0);
	}
	int dfn[maxn];
	int end[maxn];
	void DFS(int node)
	{
		dfn[node] = ++stamp;
		wander(G, node)
		{
			DEF(G);
			DFS(to);
		}
		end[node] = stamp;
	}

	BIT bit;
	void query()
	{
		int cntq = 1;
		int cnt = 0;
		for (char* ch = origin; *ch; ch++)
		{
			if (*ch == 'B')
			{
				bit.add(dfn[cnt], -1, size);
				cnt = nodes[cnt].parent;
			}
			else if (*ch == 'P')
			{
				while (cntq <= q && pos[querys[idx[cntq]][1]] == cnt)
				{
					register int t = querys[idx[cntq]][0];
					ans[idx[cntq]] = bit.query(dfn[pos[t]], end[pos[t]]);
					cntq++;
				}
			}
			else
			{
				int x = code(*ch);
				cnt = nodes[cnt].ch[x];
				bit.add(dfn[cnt], 1, size);
			}
		}
	}
} ac;

void run()
{
	scanf("%s", origin);
	m = strlen(origin);
	ac.build();

	q = readIn();
	for (int i = 1; i <= q; i++)
	{
		querys[i][0] = readIn();
		querys[i][1] = readIn();
	}

	for (int i = 1; i <= q; i++)
		idx[i] = i;
	std::sort(idx + 1, idx + 1 + q, comp);

	ac.initACAutomaton();
	ac.buildGraph();
	ac.DFS();
	ac.query();
	for (int i = 1; i <= q; i++)
		printOut(ans[i]);
}

int main()
{
	run();
	return 0;
}
总结

AC 自动机也有后缀链接树,这道题对统计多个串之间的出现次数有很大的启发。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Luogu 2414 [NOI 2011] 阿狸的打字机 的相关文章

  • 2011,我和CSDN亲密接触的一年

    从CSDN刚刚发出这次征文活动的时候 xff0c 就有一种想参加的冲动 xff0c 总想说些什么 xff0c 迟迟直到今天才开始下笔 和大家一样 xff0c 我也是一名普通的计算机研发人员 xff0c 说挨踢者也行 xff0c 说码农亦可
  • ITIL 2011 -- 服务运营的5个流程简介 (上)

    要做一个IT运维管理的项目 xff0c 客户提到了ITIL xff08 IT Infrastructure Library xff09 xff0c 所以谈需求之前我研究了一下ITIL xff0c 发现东西比较多 xff0c 但是里面的服务运
  • luogu P2078 朋友

    题目背景 小明在A公司工作 xff0c 小红在B公司工作 题目描述 这两个公司的员工有一个特点 xff1a 一个公司的员工都是同性 A公司有N名员工 xff0c 其中有P对朋友关系 B公司有M名员工 xff0c 其中有Q对朋友关系 朋友的朋
  • luogu P1593 因子和

    不要吐槽博主总做这些数论氵题 首先我们看到这种因数问题 果断质因数分解 所以当前数 a 61 p 1 k 1 p 2 k 2 p m k m 可得 a b 61 p 1 k 1 b p 2 k 2 b p m k m b 考虑因数和 假设数
  • Luogu 3647 [APIO 2014] 连珠线

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 又看错题了 题目中说一个新的珠子和一个已经添加的珠子连接起来 xff0c 我没有看到 xff0c 然后就凉了 立个 flag xff1a 已经连续看错五题了 xff0c
  • Luogu 3645 [APIO 2015] 雅加达的摩天楼

    传送门思路正解参考代码Update 传送门 思路 唉 xff0c 我太弱了 xff0c 我都看出来要分块了 xff0c 就是做不来 不过终于把题读对了 先来看子任务三怎么做 显然可以有一个 O m 2 O m 2
  • Luogu 3778 [APIO 2017] 商旅

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 看到这道题就想到了二分答案找负环 xff0c 但是怎么做呢 xff1f 完全不会 唉 xff0c 我太弱啦 xff01 先注意题目中说可以重复经过点和边 x
  • Luogu 2168 [NOI 2015] 荷马史诗

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连哈夫曼树都不会 这道题就是一个 k k 叉哈夫曼树 题目要求满足两个条件 一是代价最小 二是最长长度最小 最长长度最小很好解决 只需要优先合并
  • Luogu 3822 [NOI 2017] 整数

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 当年网同这道题还拿了 16 16 分 xff0c 现在一分都不会做了 xff0c 唉 xff0c 我太弱啦 xff01 这道题其实是很不错的 x
  • Luogu 2354 [NOI 2014] 随机数生成器

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 这么一个傻逼题 xff0c 我却看成了要你构造一种交换方案使得答案最小 xff0c 结果后面的额外交换是题目给定的 唉 xff0c 我太弱啦 x
  • Luogu 2414 [NOI 2011] 阿狸的打字机

    文章目录 传送门思路参考代码总结 传送门 思路 首先我们甚至不能单独保存每个字符串 xff0c 因为总长度可以达到 O n 2
  • NOI 2018 游记

    Day 2Day 1Day 0Day 1Day 2Day 3Day 4Day inftyDay 5 Day 2 昨天打完了最后一个一场模拟赛 xff0c 又垫底啦 xff01 本来之前我很少垫底的 xff0c 不知道为什么最后四场模拟赛一直
  • 我的2011 憧憬2012

    逝者如斯夫 不舍昼夜 2012已经向我们走来 xff0c 我们面对2011的离开 xff0c 稍有不舍 xff1b 但是人总得往前走 xff0c 微笑迎接2012 xff0c 注定我们在2012收获的更多 2011 xff0c 写给宿舍的哥
  • CentOS8.3.2011无法联网解决方案

    1 切换到ifcfg ensXX目录下 cd etc sysconfig network scripts 2 编辑ifcfg ensXX文件 vim ifcfg ens33 3 修改 BOOTPROTO 61 dhcp 并且修改 ONBOO
  • 我的2011——毕业之年的总结与彷徨

    题记 眼看2011即将成为过去 xff0c 难得在这最后的时刻 xff0c 抽点时间 xff0c 倒上一杯热茶 xff0c 回忆这一年的浮浮沉沉 这一年 xff0c 我和所有毕业生一样 xff0c 离开了呆了四年的大学校园 呆腻了校园的生活
  • 2011,我和CSDN亲密接触的一年

    从CSDN刚刚发出这次征文活动的时候 xff0c 就有一种想参加的冲动 xff0c 总想说些什么 xff0c 迟迟直到今天才开始下笔 和大家一样 xff0c 我也是一名普通的计算机研发人员 xff0c 说挨踢者也行 xff0c 说码农亦可
  • 再见2011,你好,2012。

    不会写文章 xff0c 这个算是对自己的一个总结吧 xff0c 毕业一年半了 xff0c 从事嵌入式也有一年半了 xff0c 总觉得自己连入门都谈不上 xff0c 整天都看上去很忙 xff0c 有时候确实有一大堆的事情要做 xff0c 但是
  • 我的2011----再见2011!你好2012!

    今天本来是 特别平常的一天 但是因为位置排在了 2011 年的最后 平常也就变得不平常了 一年就在这么转眼即逝中度过了 虽说一年比较短暂 但是回头在看看自己所拥有的这一年 留下的很多 在 2011 我把 ShortBrain 英语进行着 英
  • BW笔记(2011-10-24更新至No.237)

    1 同一个变量名的UID可能有多个 xff0c 记得注意 2 在查找时要注意技术名称还是名称 xff0c 因为查询时会在两个中进行 xff0c 模糊查询时要细心 xff0c FV与V都可以查到 3 复制的时候注意长度 xff0c 过长的会不
  • [NOI 2015复习][BZOJ 1509][NOI 2003]逃学的小孩(树的直径)

    题目链接 http www lydsy com JudgeOnline problem php id 1509 题目大意 要从一棵树中找出三个点 X Y Z X Y Z 使得 min dis A C dis B C dis A B min

随机推荐

  • Luogu 2150 [NOI 2015] 寿司晚宴

    传送门思路对于 30 30 30 的数据对于 100 100 100 的数据参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 完全做不来 xff0c 连暴力都打不来 主要好像是因为我从来没有做过以质因子为
  • Luogu 3649 [APIO 2014] 回文串

    传送门思路Manacher 算法 特殊字符回文半径算法与实现本质不同的回文串个数 正解参考代码总结 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 这道题各路神仙都说是模板题 xff0c 但我觉得完全不可做 xf
  • Luogu 2168 [NOI 2015] 荷马史诗

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连哈夫曼树都不会 这道题就是一个 k k 叉哈夫曼树 题目要求满足两个条件 一是代价最小 二是最长长度最小 最长长度最小很好解决 只需要优先合并
  • Luogu 2178 [NOI 2015] 品酒大会

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 做了两个星期的题 xff0c 自己做出来的才只有这一道 xff0c 唉 xff0c 我太弱啦 xff01 我们考虑第一问怎么做 题目中相似的概念
  • Luogu 1117 [NOI 2016] 优秀的拆分

    传送门思路利用后缀数组解决重复子串问题注意事项参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连暴力都想不到 xff0c 唉 xff0c 我太弱啦 xff01 考虑暴力法 xff0c 可以枚举一个中间点
  • Luogu 1712 [NOI 2016] 区间

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 这么个傻逼题 xff0c 居然把离散化写错了 xff0c 唉 xff0c 我太弱啦 xff01 显然我们可以考虑枚举最短长度和最长长度 xff0
  • CF 977F Consecutive Subsequence

    传送门思路参考代码 传送门 思路 CF 的第一场 div3 xff0c 在我提交了一份有错的代码后突然不能提交了 xff0c 在跑什么 System Testing xff0c 我就跟它杠上了 xff0c 直到它评测完 唉 xff0c 我太
  • CF 7D Palindrome Degree

    传送门思路参考代码 传送门 思路 不是马拉车加随便 DP 乱搞 xff1f 本来想复习一下马拉车的 xff0c 结果拉出了许多事端 xff08 修复了 OI Learner Judge 的严重 bug 一个害我调了两节课的 bug xff0
  • Luogu 3822 [NOI 2017] 整数

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 当年网同这道题还拿了 16 16 分 xff0c 现在一分都不会做了 xff0c 唉 xff0c 我太弱啦 xff01 这道题其实是很不错的 x
  • 【Go】go语言中切片的长度变化后容量的变化

    一 新增信息长度 43 当前长度 lt 61 当前容量 span class token keyword func span span class token function printSlice span span class toke
  • APIO 2018 Practice Session T1 Wedding cake

    没有传送门题目大意思路参考代码熟悉环境 没有传送门 题目大意 给你一个长度为 n n 的正整数序列 a i ai xff0c 要求构造出 n n 个小数 使得它们的和为 1 1 xff0c 且每个数小数点后恰好有
  • APIO 2018 Practice Session T3 / CF 936C Lock Puzzle

    传送门题目大意思路参考代码总结 传送门 题目大意 给你一个字符串 origin xff0c 一个字符串 target xff0c 长度均为 n n 要求在 3 n 3n xff08 5 2 n 5 2
  • APIO 2018 游记

    Day 0Day 1Day 2Day 3Day 4 Day 0 早上 4 4 点就上车去机场赶那 7 7 点的飞机 感觉很困 xff0c 所以在飞机上就这么睡过去了 北京是个好地方 xff0c 但是与我无关 下飞机后 xff0c 我们一行人
  • Luogu 2375 [NOI 2014] 动物园

    文章目录 传送门思路参考代码Review 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连 KMP 也不会 xff0c WA 飞了 xff0c 唉 xff0c 我太弱啦 xff01 首先 xff0c 原始的 K
  • Luogu 2114 [NOI 2014] 起床困难综合症

    传送门思路参考代码 传送门 思路 按位贪心 但是我太弱了 xff0c 明明可以 O n O n 预处理 xff0c 我却只会 O 32 n O
  • Luogu 2354 [NOI 2014] 随机数生成器

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 这么一个傻逼题 xff0c 我却看成了要你构造一种交换方案使得答案最小 xff0c 结果后面的额外交换是题目给定的 唉 xff0c 我太弱啦 x
  • Luogu 2305 [NOI 2014] 购票

    传送门思路别人家的题解弱化的传送门 xff08 Luogu 3994 高速公路 xff09 参考代码 对于没有距离限制的 50 分 参考代码 对于 100 分的数据参考代码Remarks 传送门 思路 唉 xff0c 我太弱了 xff0c
  • Luogu 1224 [NOI 2013] 向量内积

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 听都没有听说过这类题 xff0c 唉 xff0c 我太弱啦 xff01 显然这道题可以在 O n 2 d O n 2
  • Luogu 1397 [NOI 2013] 矩阵游戏

    传送门思路正解参考代码Remarks 传送门 思路 唉 xff0c 我太弱了 xff0c T1 都做不来 xff0c 唉 xff0c 我太弱啦 xff01 显然这个题可以用矩阵快速幂在 O log n O log n
  • Luogu 2414 [NOI 2011] 阿狸的打字机

    文章目录 传送门思路参考代码总结 传送门 思路 首先我们甚至不能单独保存每个字符串 xff0c 因为总长度可以达到 O n 2