IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【第九题】礼物(北理工/北京理工大学/程序设计方法与实践/小学期) -> 正文阅读

[数据结构与算法]【第九题】礼物(北理工/北京理工大学/程序设计方法与实践/小学期)

目录

前言

题干:【礼物】

测试用例:

心路历程

1 自己尝试TLE

2 论坛无果

3 柳暗花明

子序列自动机怎么学?

1 为什么要子序列自动机?

2 学习资料

3 补充思路

代码



前言

本文面向无竞赛或者算法基础的人,或者说,当你来搜这个题目的时候,我就默认你没有相关基础了。为什么写这篇博客呢?其实我觉得我是不配的,但是csdn上我这篇大概将会成为唯一一篇这道题的题解。(之前是有一篇的,但是因为题目有一些变化,所以那篇文章已经可以说是过时了)

建议顺着把文章看完,我个人理解,如果可以跳过前面直接到后面,那也不会面向CSDN编程了。

本人平平无奇,所以不会出现大佬们的视角盲区,各位平起平坐,放心食用,不用担心看不懂之类的。

我的基础具体说,只有假期自己看了300页c primer plus的基础,不会c++,不会STL,不会算法,只会c语言基础语法一把梭,大家可以对接一下自己的水平,我觉得这就是个中等水平。

我想说的是,不会可以学,不会并不代表不能会,英雄不问出处,请坚持住!

这道题搞了我几个小时,终于是完美地AC了,现在回头敲出来不会花很长时间,其实时间都花在学习新知识上了,下一道题根据@贝贝今天AC了吗 的文章,还要学数据结构的栈,头秃,就当是提前学吧,反正这小学期就是个提取学习,见到啥学啥。

本题解不是最优解,好在思路比较清晰,能稳定通过全部案例。


题干:【礼物】

Description

小张的好朋友小松要过生日了,小张打算为他挑选一件礼物。在市场上他发现有一个珠子手镯的商店很不错。在这家商店会出售特殊的珠子并穿成一个手镯,在货架上珠子排成一排,每一个珠子上有一个小写英文字母。店家有一个特殊的规定,必须在一排珠子中按顺序从左到右挑选。小张心中已经有一个想要送给小松的单词,请你告诉他应该如何挑选珠子使得手镯上珠子的字母组成小张想要的单词。

Input

第一行,一个字符串,表示货架上的一排珠子,仅包含小写英文字母,长度在200000以内。

第二行,一个字符串,表示小张想要的单词,仅包含小写英文字母,长度在10000以内。

Output

输出一行整数,表示小张按照从左到右需要挑选的珠子在货架上的位置。

Notes

从左到右按顺序选出的珠子上的字母为'p','p','y','h','a'。串成环形的手镯后可以组成"happy"。

数据保证有解,若有多种选取方法,输出其中字典序最小的一个,比如1 2 3 4比1 3 4 5的字典序小

提交后查看结果页面错误信息一栏,前4行的编译错误大家不用理会,第5行是关于你的结果的信息。

测试用例:

input

????????pxrtpsapyjhuvab

????????happy

output

????????1?5?9?11?14


心路历程

1 自己尝试TLE

其实这道题乍一看很简单,不就是遍历吗。

然而TLE路上等你,说多了都是泪。

2 论坛无果

论坛的信息太过零散,大多是一种个人总结,带有装逼性质的那种,顺便混点分。这种不会讲太细,看不懂是很正常的。比如下面这个,现在回头看,他说的都是非常正确的,而且是很关键的,但是当时怎么就看不懂呢?关键出在基础知识上。

3 柳暗花明

无意间看到有人说子序列自动机,我依稀想起舍友之前和我说过这个东西。

当时舍友和我说的时候我上csdn看了看,感觉太难了,就没再看过,结果最后碰的头破血流,回头发现当时看起来最难的东西,可以说几乎是做出这道题AC的唯一途径,否则就是TLE。

于是就开始漫长的子序列自动机的学习。


子序列自动机怎么学?

1 为什么要子序列自动机?

逐字符遍历耗时太久,我们需要跳跃。

跳到哪里呢?比如我要找happy,我现在已经找到了h,那我能不能直接跳到后面第一个a呢?可以,只要我们储存了那个a的编号就可以,子序列自动机应运而生。

2 学习资料

https://blog.csdn.net/qq_43109145/article/details/89429411?spm=1001.2014.3001.5506

这篇文章因为有图解,所以比较清晰易懂,但是有一个重大缺陷,那就是这个程序会跑错(破防),也就是说,她的思想是对的,但是有细节错误,所以这篇文章就供大家建立对子序列自动机的基本认识:横坐标代表什么?纵坐标代表什么?如何根据给出的带筛选字符串构造一个子序列自动机二维数组?如何用取出来的一种环(比如appyh)去跳跃遍历这个子序列自动机?

剩下的文章,大家也可以搜着看,核心都是一样的,就是建立自动机,然后遍历。

3 补充思路

其实要看懂自动机,最好还是有个思路在前。如果没有一个宏观思路,那么去看代码很容易只见树木不见森林,这里提供一下宏观思路:

首先要遍历所有可能的组合,类似:happy,appyh,直到yhapp

我们可以使用happyhappy字符串,从h到y每次顺次读取5个字符就可以实现,如果不想扩展字符串也可以用%运算来实现模仿环形。

对于每一种可能的5个字符,要在自动机中遍历,自动机的作用简单说就是加快遍历的速度,从一个一个遍历,到跳跃遍历。

好,如果能成功遍历到我们目标的5个字符,那么这个组合就是有解的。

之后就要判断是否是最优解了,这里要用一个ans数组储存最终的答案,用temp数组去做缓冲。这里涉及到一个字典序的的定义,字典序大家百度一下,其实就是类似于strcmp函数,只不过我们比较的内容超出了char范围,所以手写一个对int生效的“intcmp”函数,如果temp的字典序小于ans,那么就是更优解,那么就把temp写入ans,以此类推。

最后输出ans即可,因为不断优化,最后剩下的就是最优解了。

好的,到这里,就把大致的思路介绍完了,那么之后无论是学习子序列自动机或者看我的代码,都可以比较容易的看懂。


代码

请注意,不要ctrl c+v不要ctrl c+v,不要ctrl c+v!!!!

懂的都懂,查重直接爆炸,扣十分可能直接挂科,。

至少改变量,加自己的想法,或者尝试把自动机写成不包括自己的形式,都可以。我想,能来北理工的,怎么也得有点傲骨的吧,看我的代码是作为参考,抄就是人格的问题了。

如果你真的想学会这个知识点的话,建议你看懂我的代码后自己敲出来,或者对着我的代码先敲一遍,一边敲一边打注释,来辅助理解,最后删掉再自己敲出来,那就算是学会了。

我加了很多注释,这些注释其实是我在学习过程中记录的思考,同时大大提高程序可读性,大概csdn上像我这样几乎一句一个注释的二五仔少得很吧,但是为了理解,脸可以先放一边。

#include<stdio.h>
#include<string.h>
#include<stdbool.h>
#include<limits.h>
#define XLEN 200010
#define WORDLEN 10010
//上面两个都稍微开大点,不会越界
char x[XLEN]; //x储存待筛选字符串
bool used[XLEN];//used用来记录字符是否已经被使用过
char words[WORDLEN * 2]; //words开两倍,为了放两倍
int next[XLEN][26];//next是灵魂,子序列自动机
int ans[WORDLEN]; //ans是最优解
int temp[WORDLEN]; //temp是解
int main(void)
{
	int len_x, len_word;
	int i,j; //ijk用来计数循环
//	freopen("input.txt", "r", stdin); //0 测试重定向

	memset(next, -1, sizeof next); 
	scanf("%s%s", x, words);
	len_word = strlen(words);
	len_x = strlen(x);
	for (i = 0; i < len_word; i++)//初始化字典序,用最大字典序
		ans[i] = INT_MAX;
	strncat(words, words, 2*WORDLEN-len_word-1); //以双倍链代环
	//构造序列自动机
	//next记录的是从自己当前位开始以后的真实位置
	//i从strlen-1到0,不断继承上一个,继承后把自己所在的位置更新一下即可
	//因为数组故意开大,所以不会越界
	//其实我这里纠结过当前next应不应该包括自己,后来选择了包括
	//其实也有不包括的做法
	for (int i = strlen(x)-1; i >= 0; i--) 
	{
		for (int j = 0; j < 26; j++) 
			next[i][j] = next[i+1][j];
		next[i][x[i] - 'a'] = i;
	}
	//对所有环做遍历
	for (i = 0; i < len_word; i++)
	{
		int node = 0; //对一个环的情况,从头遍历到尾部
		int k = 0;//k用来辅助存放位置
		bool isloop = true; //每次假设可以成环
		bool isbetter = true; //假设每次都是更优解
		memset(used, 0, sizeof used); //每次都要刷新使用情况
		for (j = 0; j < len_word; j++)
		{
			if (used[node]) //修正保留当前位置带来的影响
				node++;
			node = next[node][words[i + j] - 'a'];
			temp[k++] = node + 1;//+1就存放了实际位置
			used[node] = true;
			if (node == -1) //后面没有了
			{
				isloop = false;
				break;
			}
		}
		if (isloop) //成功走完一圈,temp已经装好,计算最优解
		{
			//比较字典序,因为用的INT_MAX,第一次比较必定成功
			for (k = 0; k < len_word; k++)
				if (temp[k] > ans[k])
				{
					isbetter = false;
					break;
				}
				else if (temp[k] < ans[k]) 
					break;
			//必须加提前退出机制,否则就比如 temp 2 5 9 10和  ans 2 6 8 9
			//虽然没有提前退出,导致9>8反而判定字典序小
			//这个问题是基础不牢,比较字典序应该是基本功,分成三种情况
			//一个大于另一个就直接判断出来,相等才进行下一步
			//而不是把另外两种情况轻率的合并
			if (isbetter)//是更优解则写入ans
				for (k = 0; k < len_word; k++)
					ans[k] = temp[k];
		}
	}
//	printf("最优解:"); //3 测试
	for (i = 0; i < len_word; i++)//本来想用k的,毕竟k含义就是存位置的,但是k被释放了
		printf("%d%c",ans[i],i==len_word-1?'\n':' ');

	return 0;
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-09-02 11:37:50  更:2021-09-02 11:38:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 0:49:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码