ICPC SWERC 2020 K - Unique Activities(SAM记录子串第一次出现的位置 or SAM + hash)

news2024/10/5 13:07:41

两种做法的效率差异

做法一:SAM记录子串第一次结束位置
在这里插入图片描述
做法二:SAM + hash
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题意:

给定一个字符串,让你找到只出现过一次,且长度最短的子串并输出,如果有多个则输出最先出现的那个。

思路:

思路一:对原串构建后缀自动机,构建的同时维护一个firstpos数组,用于记录每个节点代表子串第一次出现时的结束位置(然后我们又维护了len数组,其实就等效于可以记录子串第一次出现时的开始位置了)。构建完成后在后缀链接树上跑dfs,回溯的时候计算每个节点代表子串出现的次数,当某个节点出现次数为1时,我们判断是否能够更新目标串的长度anslen和结尾下标ed,不断迭代。最终dfs完毕后,根据目标串的长度和结尾下标能直接算出开始位置sta,输出目标串即可。

思路二:对原串构建后缀自动机和字符串哈希数组,dfs的过程和思路一差不多,只不过我们只能得到目标串的长度anslen,之后对原串进行长度为anslen的尺取遍历计算每一段的哈希值,把所有哈希值丢到map里面计数,最后再尺取遍历原串,如果当前哈希值出现次数正好为1,则直接输出该段子串即可。

时间复杂度: O ( n ) O(n) O(n)

代码:

法一:

#include<bits/stdc++.h>

using namespace std;
const int N = 3e5 + 10, M = N << 1;
int ch[M][26], fa[M], len[M], fipos[M], np = 1, tot = 1;//其中新增一个fipos数组用于记录SAM中每个节点所代表的子串 “第一次出现时的结束位置下标”
long long cnt[M];
char s[N];
vector<int> g[M];
int anslen, ed = 0;	//目标串的长度和结束位置

void extend(int c) {
	int p = np; np = ++tot;
	len[np] = len[p] + 1, cnt[np] = 1, fipos[np] = len[np] - 1;	//如果下标从0开始则减一
	while (p && !ch[p][c]) {
		ch[p][c] = np;
		p = fa[p];
	}
	if (!p) {
		fa[np] = 1;
	}
	else {
		int q = ch[p][c];
		if (len[q] == len[p] + 1) fa[np] = q;
		else {
			int nq = ++tot;
			len[nq] = len[p] + 1;
			fa[nq] = fa[q], fa[q] = fa[np] = nq;
			fipos[nq] = fipos[q];	//每次复制的节点nq的fipos因为是从一个节点q中分出来的,所以还是q的fipos值。
			while (p && ch[p][c] == q) {
				ch[p][c] = nq;
				p = fa[p];
			}
			memcpy(ch[nq], ch[q], sizeof ch[q]);
		}
	}
}

void dfs(int u)
{
	for (auto son : g[u]) {
		dfs(son);
		cnt[u] += cnt[son];
		fipos[u] = min(fipos[u], fipos[son]);	//不加这句也可以过
	}
	if (cnt[u] == 1 && u > 1) {	//当当前节点代表子串只出现过一次
		if (anslen > len[fa[u]] + 1) {	//满足这个条件就可更新目标串的长度和结束位置
			anslen = len[fa[u]] + 1;
			ed = fipos[u];
		}
		else if (anslen == len[fa[u]] + 1) {	//这个判断是一定要加的,因为是在树上进行,而不是在一个数组上
			ed = min(ed, fipos[u]);
		}
	}
}

signed main()
{
	scanf("%s", s);
	anslen = strlen(s);
	for (int i = 0; s[i]; ++i) extend(s[i] - 'A');
	for (int i = 2; i <= tot; ++i) g[fa[i]].emplace_back(i);
	dfs(1);
	int sta = ed - anslen + 1;	//目标串的开始位置
	for (int i = sta; i <= ed; ++i) {
		putchar(s[i]);
	}
	puts("");

	return 0;
}

法二:

#include<bits/stdc++.h>

using namespace std;
typedef unsigned long long ull;
const int N = 3e5 + 10, M = N << 1, P = 1331;
ull p[N], hah[N];
int ch[M][26], fa[M], len[M], np = 1, tot = 1;
long long cnt[M];
char s[N];
vector<int> g[M];
int anslen = N;
int n;
unordered_map<ull, int> ha;

ull get(int l, int r) {
	return hah[r] - hah[l - 1] * p[r - l + 1];
}

void init() {
	p[0] = 1;
	n = strlen(s);
	for (int i = 1; i <= n; ++i) {
		p[i] = p[i - 1] * P;
		hah[i] = hah[i - 1] * P + s[i - 1];
	}
}

void extend(int c) {
	int p = np;
	np = ++tot;
	len[np] = len[p] + 1, cnt[np] = 1;

	while (p && !ch[p][c]) {
		ch[p][c] = np;
		p = fa[p];
	}

	if (!p) {
		fa[np] = 1;
	}
	else {
		int q = ch[p][c];
		if (len[q] == len[p] + 1) fa[np] = q;
		else {
			int nq = ++tot;
			len[nq] = len[p] + 1;
			fa[nq] = fa[q], fa[q] = fa[np] = nq;

			while (p && ch[p][c] == q) {
				ch[p][c] = nq;
				p = fa[p];
			}
			memcpy(ch[nq], ch[q], sizeof ch[q]);
		}
	}
}

void dfs(int u)
{
	for (auto son : g[u]) {
		dfs(son);
		cnt[u] += cnt[son];
	}
	if (cnt[u] == 1) {
		anslen = min(anslen, len[fa[u]] + 1);
	}
}

signed main()
{
	scanf("%s", s);
	init();
	for (int i = 0; s[i]; ++i) extend(s[i] - 'A');
	for (int i = 2; i <= tot; ++i) {
		g[fa[i]].emplace_back(i);
	}
	dfs(1);
	int l = 1, r = min(l + anslen - 1, n);
	while (r <= n) {
		ull tmp = get(l, r);
		ha[tmp]++;
		l++, r++;
	}
	l = 1, r = min(l + anslen - 1, n);
	while (r <= n) {
		ull tmp = get(l, r);
		if (ha[tmp] == 1) {
			for (int i = l - 1; i <= r - 1; ++i) {
				printf("%c", s[i]);
			}
			puts("");
			return 0;
		}
		l++, r++;
	}

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/426258.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

迷宫问题-DFS-BFS

迷宫问题迷宫问题简介BFS解决迷宫最短路径问题DFS记录迷宫路径DFS解决迷宫所有路径问题迷宫问题简介 &#x1f680;学习过算法程序设计的应该都学习过迷宫这个问题&#xff0c;迷宫问题主要设计的算法就是DFS-深度优先遍历和BFS-广度优先遍历。 &#x1f680;在一个二维数组中…

Jmeter和Testlink自动化测试框架研究与实施

摘 要 目前基于Jmeter的接口自动化测试框架&#xff0c;大多只实现脚本维护和自动调度&#xff0c;无法与Testlink进行互通&#xff0c;实现测试方案与自动化实施流程连接&#xff0c;本文基于Testlink、Jmeter、Jenkins实现&#xff1a;通过Testlink统一维护接口自动化测试用…

搭建es集群

单点es的缺点&#xff1a;1.存储数据有限&#xff1b;2.单点故障问题&#xff08;es出现故障则整个服务会直接宕机&#xff09; 解决存储数据有限&#xff1a;搭建多台es服务器实现集群 解决单点故障&#xff1a;在不同的es服务器中进行备份数据&#xff08;例&#xff1a;在…

车载测试需要有哪些知识需要学习的?

一、车载行业前景 其中的车载测试也随着国家对新能源、智能驾驶等领域的支持&#xff0c;而异常活跃&#xff0c;目前我国共有9000家自动驾驶相关企业&#xff0c;而今年从华为、中兴、大唐等通信领域的企业到以阿里、腾讯、小米等为代表的互联网企业&#xff0c;均已布局自动…

浅谈继承和发扬传统文化路径

中华民族五千年文明历史,造就了博大精深的中华传统文化。如何继承和发扬传统文化,是当代文化爱好者和工作者所关注的&#xff0c;实现这一使命,文化传承需要在以下路径上发力: 1. “微更新”路径。在传承的基础上进行融合拓展,实现内涵丰富和更新。 2. “强保护”路径。利用立…

空压机远程监控系统解决方案

一、项目背景 随着物联网各种技术快速发展,各物联网远程监测应用场景也应用而生&#xff0c;空压机是一种空气压缩和气体输送设备&#xff0c;广泛运用于矿山、机械、电子、医疗等各行业。空压机常规都是需要人在现场监测和维护,现在通过物联网技术,远程监控成为可能&#xff…

利用Msray-plus提升SEO工作效率和效果

随着互联网的不断发展和普及&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;已经成为了网站推广和营销的重要手段之一。SEO的核心是通过优化网站的结构、内容和关键词等&#xff0c;提高网站在搜索引擎中的排名&#xff0c;从而吸引更多的访问量和潜在客户。然而&#xf…

多通道振弦传感器无线采集采集仪如何开始使用

多通道振弦传感器无线采集采集仪如何开始使用 开始使用 设备电源 VS208~432 可使用内置电池&#xff08;默认&#xff09;也可使用外部电池工作。 需要特别注意&#xff1a;严禁内置和外部电池&#xff08;电源&#xff09;同时使用&#xff0c;严重时会造成短路起火&#xff0…

宝塔搭建chevereto图床

前言 chevereto是一个国外开发的图床&#xff0c;使用phpnginxmysql搭建的&#xff0c;使用宝塔面板更方便搭建chevereto图床&#xff0c;chevereto有收费和免费版本 准备环境 宝塔面板&#xff0c;百度网上很多教程&#xff0c;一键安装.chevereto安装包&#xff0c;本次使…

Cadence Allegro PCB设计88问解析(二十七) 之 Allegro中dimension environment命令使用(添加及删除尺寸标注)

一个学习信号完整性仿真的layout工程师 在通常的设计中&#xff0c;一般会在outline的光绘层中添加单板或则拼版外形的尺寸大小。方便板厂人员查看&#xff0c;但是尺寸标注的添加涉及到尺寸单位、精度等&#xff0c;要根据公司的标准添加。如果需要修改尺寸的参数&#xff0c;…

nssctf web 入门(7)

这里通过nssctf的题单web安全入门来写&#xff0c;会按照题单详细解释每题。题单在NSSCTF中。 想入门ctfweb的可以看这个系列&#xff0c;之后会一直出这个题单的解析&#xff0c;题目一共有28题&#xff0c;打算写10篇。 [SWPUCTF 2021 新生赛]hardrce [SWPUCTF 2021 新生赛…

【快乐手撕LeetCode题解系列】——环形链表

【【快乐手撕LeetCode题解系列】——移除链表元素&#x1f60e;前言&#x1f64c;环形链表&#x1f64c;画图分析&#xff1a;&#x1f60d;思路分析&#xff1a;&#x1f60d;源代码分享&#xff1a;&#x1f60d;总结撒花&#x1f49e;&#x1f60e;博客昵称&#xff1a;博客…

python简单认识一下字面量并尝试编写输出字面量在控制台

首先 还是顾名思义 什么是字面量&#xff1f; 在代码中写下来的固定的值 我们称之为 字面量 python的字面量主要是以下几种 当然 前期不需要那么急 我们先熟悉 数字类型中的 整数 浮点数 和字符串类型 然后后续再慢慢扩展即可 整数呢 就相当于 我们数学中的 正整数 例如 1 10…

Compose (10/N) - 动画

一、高级别动画 1.1 简单值动画 animate***AsState 为单个值添加动画。只需要指定目标值&#xff0c;会从当前值向目标值渐变。 animateColorAsStateanimateDpAsStateanimateSizeAsStateanimateOffsetAsStateanimateRectAsState animateIntAsState animateIntOffsetAsState an…

【机会约束、鲁棒优化】机会约束和鲁棒优化研究优化【ccDCOPF】研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【数据挖掘与商务智能决策】第十章 支持向量机

1. 线性可分SVM import numpy as np import pandas as pd import matplotlib.pyplot as plt%matplotlib inline1.1 生成模拟数据 # 导入sklearn模拟二分类数据生成模块 from sklearn.datasets import make_blobs # 生成模拟二分类数据集 X, y make_blobs(n_samples150, n_f…

SIP协议之RTP负载类型(payload type)

一、RTP负载类型介绍​ 在SIP 音视频通话中&#xff0c;媒体数据是由RTP包携带的&#xff0c; RTP包中的PT字段标识了负载媒体数据的类型。如下图&#xff1a; 注&#xff1a; PT表示负载类型(Payload Type), 7 bits&#xff0c;即所传输的多媒体的类型 ​  不同的媒体编码…

Kafka消费者组和分区再均衡

应用程序使用KafkaConsumer向Kafka订阅主题&#xff0c;并从订阅的Topic上接收消息。 要想知道如何从Kafka读取消息&#xff0c;需要先了解消费者和消费者组的概念。 1、消费者和消费者组 原因&#xff1a;假设我们有一个应用程序需要从一个Kafka Topic中读取消息并验证&…

14Exceptional Control Flow Exceptions and Process(异常控制流,异常和进程)

异常控制流 异常控制流出现的地方&#xff1a; 异常控制流&#xff08;Exceptional Control Flow&#xff0c;ECF&#xff09;是程序执行过程中由于某些特殊事件或条件而导致的控制流的改变。异常控制流通常出现在以下几种情况&#xff1a; 硬件异常和中断&#xff1a;硬件异…

14天手撸交互式问答数字人直播教程-课程计划

一、课程计划 二、时间安排 第01天&#xff1a;交互式问答数字人发展现状 从一个真实案例开始&#xff0c;介绍当前主流的交互式数字人平台&#xff0c;需求和应用场景&#xff0c;引入交互式数字人的交互流程和关键技术。后续整个直播系列的内容安排。 第02天&#xff1a;音…