当前位置: 首页 > news >正文

【扩展KMP】P10634 BZOJ2372 music |省选-

本文涉及知识点

较难理解的字符串查找算法KMP

标题

P10634 BZOJ2372 music

题目描述

最近 A、B 两国发生了一场战争。dick 作为 A 国的军事总指挥,最近非常头痛于己方的情报问题。因为 B 国最近雇佣了 Easy 这一位密码专家来给他们的所有通讯加密。

Easy 非常喜欢唱歌,于是他决定将所有的信号都变成旋律储存起来,比如说 11556654433221 11556654433221 11556654433221 就可能是一段加过密的音符,我们用一个等长度的序列来表示它,就变成了 1 , 1 , 5 , 5 , 6 , 6 , … 1,1,5,5,6,6,\dots 1,1,5,5,6,6,。为了增加密码的保密性,他把加密的乐谱又调整了一下,把某些音调改变了,将原序列 A A A 变成 B B B,有 ∣ A ∣ = ∣ B ∣ |A|=|B| A=B,且对于 a i = a j a_i=a_j ai=aj b i = b j b_i=b_j bi=bj,对于 a i < a j a_i<a_j ai<aj b i < b j b_i<b_j bi<bj,对于 a i > a j a_i>a_j ai>aj b i > b j b_i>b_j bi>bj。例如:1122155775 就可能代表了同一段音符。

最近,dick 截获了一段信号,这段信号中可能包含了某些重要信息。根据以往的经验,dick 已经知道了某些旋律所代表的意义。于是 dick 想知道,对于一段已知的旋律,能不能判断它是否在这段截获的旋律中出现?如果出现了,能否找出它出现的次数及位置呢?

「任务」判断给定旋律在截获旋律中出现的次数及位置。

输入格式

第一行三个正整数 n , m , s n,m,s n,m,s n n n 是截获旋律的长度, m m m 是已知旋律的长度,所有的旋律都是 1 ∼ s ( s ≤ 25 ) 1\sim s(s\leq 25) 1s(s25) 的正整数。

接下来 n n n 行,每行一个整数描述截获的旋律 A A A

接下来 m m m 行,每行一个整数描述已知的旋律 B B B

输出格式

第一行一个整数 t t t 表示出现的次数。

然后 t t t 行,按照从小到大给出出现时的起始位置 p p p,即: A [ p … p + m − 1 ] A[p\dots p+m-1] A[pp+m1] 等价于 B B B

输入输出样例 #1

输入 #1

9 6 10
5
6
2
10
10
7
3
2
9
1
4
4
3
2
1

输出 #1

1
3

说明/提示

对于所有数据,保证 1 ≤ n ≤ 1 0 5 1\leq n \leq 10^5 1n105 1 ≤ m ≤ 25000 1\leq m \leq 25000 1m25000

扩展KMP

一,将B离散化,最小的数字改成字符a,次小的数字改成b ⋯ \cdots 。创建字符串26个字符串bb,如果b[j]=‘a’+i,则bb[i][j]=‘1’;否则bb[i][j]=0。
二,pos[i]记录B中’a’+i第一次出现的下标。
三,类似bb,根据a创建26个字符串aa。
四,判断a[i…i + m - 1]是否等效于b的逻辑
一,判断相等的是否相等。aa[i + pos[k]][i…i + m - 1]和bb[k]是否相等。利用z函数。ba[i][j] = bb[i] + aa[j] * *空间复杂度 * *:O((n + m)26 * 26)
二,判断大小关系。a[i + pos[k]] < a[i + pos[k + 1]]。
时间复杂度:O(ss(n+m))

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t);
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	cin >> n;
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
	vector<T> ret;
	T tmp;
	while (cin >> tmp) {
		ret.emplace_back(tmp);
		if ('\n' == cin.get()) { break; }
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

class KMPEx
{
public:
	template<class T>
	static vector<int> ZFunction(const T* p, int n) {
		vector<int> z(n);
		z[0] = n;
		for (int i = 1, left = 0, r = 0; i < n; ++i) {
			if (i <= r) {//如果此if,r-i+1可能为负数
				z[i] = min(z[i - left], r - i + 1);
			}
			while ((i + z[i] < n) && (p[z[i]] == p[i + z[i]])) {
				z[i]++;
			}
			if (i + z[i] - 1 > r) left = i, r = i + z[i] - 1;
		}
		return z;//z[i] 表示S与其后缀S[i,n]的最长公共前缀(LCP)的长度
	}
	static vector<int> ZFunction(string s) {
		return ZFunction(s.c_str(), s.length());
	}
	static int MinCyc(const string& str, int unit = 1) {
		const int N = str.length();
		auto z = ZFunction(str);
		auto Is = [&](int k) {
			for (int i = k; i < N; i <<= 1) {
				if (z[i] < min(N - i, i)) { return false; }
			}
			return true;
		};
		for (int k = unit; k < N; k += unit) { if (Is(k))return k; }
		return N;
	}

};

class Solution {
public:
	vector<int> Ans(const int N, const int M, const int S, vector<int>& A, vector<int>& B) {
		const int N2 = unordered_set<int>(B.begin(), B.end()).size();
		vector<int> bfirst;
		{
			vector<vector<int>> binxs(S + 1);
			for (int i = 0; i < M; i++) {
				binxs[B[i]].emplace_back(i);
			}
			for (const auto& v : binxs) {
				if (v.size()) { bfirst.emplace_back(v[0]); }
			}
		}
		vector<int> z[26][26];
		for (int i = 1; i < 26; i++)for (int j : bfirst) {
			string s(M - j + N, ' ');
			for (int k = j; k < M; k++) {
				if (B[k] == B[j]) { s[k - j] = '*'; }
			}
			for (int k = 0; k < N; k++) {
				if (A[k] == i) { s[M + k - j] = '*'; }
			}
			z[B[j]][i] = KMPEx::ZFunction(s);
		}
		auto Is = [&](int p) {
			for (int i = 1; i < bfirst.size(); i++) {
				if (A[p + bfirst[i - 1]] >= A[p + bfirst[i]]) { return false; }
			}
			for (auto& j : bfirst) {
				if (z[B[j]][A[p + j]][p + j + (M - j)] < (M - j)) { return false; }
			}
			return true;
		};
		vector<int> ans;
		for (int p = 0; p + M <= N; p++) {
			if (Is(p)) { ans.emplace_back(p + 1); }
		}
		return ans;
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	ios::sync_with_stdio(0); cin.tie(nullptr);
	int N,M,S;	
	cin >> N >> M >> S;	
		auto A = Read<int>(N);
		auto B = Read<int>(M);
#ifdef _DEBUG		
		printf("N=%d,M=%d,S=%d", N,M,S);
		Out(A, "A=");
		Out(B, "B=");
		//Out(strs2, ",strs2=");
		//Out(que, ",que=");
		/*Out(que, "que=");*/
#endif // DEBUG		
		auto res = Solution().Ans(N,M,S,A,B);
		cout << res.size() << "\n";
		for (const auto& i : res) {
			cout << i << "\n";
	}
	return 0;
}

单元测试

	int N,M,S;
		vector<int> A, B;
		TEST_METHOD(TestMethod01)
		{
			N = 6, M = 2, S = 2,A = { 1,1,2,2,3,3},B = { 2,2 };
			auto res = Solution().Ans(N,M,S,A,B);
			AssertEx({ 1,3,5 }, res);
		}
		TEST_METHOD(TestMethod02)
		{
			N = 6, M = 2, S = 2, A = { 1,1,3,3,2,2 }, B = { 1,2 };
			auto res = Solution().Ans(N, M, S, A, B);
			AssertEx({ 2 }, res);
		}
		TEST_METHOD(TestMethod03)
		{
			N = 6, M = 2, S = 3, A = { 1,1,3,3,2,2 }, B = { 3,1 };
			auto res = Solution().Ans(N, M, S, A, B);
			AssertEx({4 }, res);
		}
		TEST_METHOD(TestMethod11)
		{
			N = 9, M = 6, S = 10, A = { 5,6,2,10,10,7,3,2,9 }, B = { 1,4,4,3,2,1 };
			auto res = Solution().Ans(N, M, S, A, B);
			AssertEx({ 3 }, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

相关文章:

  • 代还app开发公司seo排名工具外包
  • 网络视频教学怎么做seo的实现方式
  • 苹果网站上物体阴影怎么做的北京公司排名seo
  • 装饰画图片大全广告优化师工资一般多少
  • 有没有专门做牛仔的网站免费个人网站建设
  • 国外常用视频网站tenor怎么设置宁德市教育局官网
  • 第 27 场 蓝桥月赛
  • 巧用sort
  • JSON工具-JSONUtil
  • 【OS】Process Management(3)
  • 蓝桥杯比赛对于时间和空间的限制
  • 进程同步和进程互斥的区别
  • 用栈实现队列
  • android audiorecord
  • PowerBI累计分析
  • Agent 开发 笔记
  • 本地项目HTTPS访问问题解决方案
  • 【后端开发】Maven
  • LeetCode热题100记录-【二分查找】
  • 单片机软件设计文档模板
  • skynet.call使用详解
  • kafka 的存储文件结构
  • fpga系列 HDL:跨时钟域同步 4-phase handshake(四相握手通信协议,请求-确认机制)浅释与代码实现
  • Boost库搜索引擎项目(版本1)
  • Nodejs回调函数
  • 使用uglifyjs对静态引入的js文件进行压缩