【优选算法 & 模拟】模拟算法入门详解 : 模拟算法小专题



替换所有的问号


题目描述



算法原理


解法:


从前往后扫描数组,当扫描到 '?' 修改字符即可;

修改的字符不要和前后两个字符相等即可;


处理细节问题


如果 '?' 在字符串最前面或者最后面,这个时候需要单独处理;


编写代码



对于这个判断条件,我们需要解析一下:


我们来解析一下这个判断条件,整体的架构:






提莫攻击


题目描述



算法原理


对于提莫相邻的两次攻击,设攻击的时间点分别是 timeSeries[ i +1 ] , timeSeries[ i ]

我们仅需要看 timeSeries[ i +1 ] - timeSeries[ i ] 的差值 x 与 duration 之间的大小关系:

    1. x >= duration,返回 ret += duration * 2

    1. x < duration,返回 ret += ( x + duration)

处理细节问题


我们一定不要忘记最后一次攻击造成的中毒时间,最终结果要累加上一次中毒时间 duration ;


编写代码



Z 字形变换


题目描述



算法原理



解法一:模拟过程


想要正确模拟过程,我们需要创建一个矩阵,行数为 n,列数是可以根据周期性计算出来的, 但是计算量非常大,所以我们直接让列数等于字符串长度即可;

我们完整的模拟过程如上,每遍历到一个字符,就按照规则放入矩形对应位置;但是这样的模拟过程,时间复杂度和空间复杂度都是非常高的;


解法二:通过模拟过程找规律


接下来,我们在刚刚模拟过程的基础上,通过总结规律,来作优化:

此时的 numRows = 4,我们设第一列相邻的两个元素(0,6,12)之间的差值为公差 d,此时 d=6;


虽然我们输入矩阵的字符顺序为N,但是我们输出的顺序是从左往右,从上到下进行的;

所以我们不需要把整个矩阵先按顺序填入,再按顺序填出,而是直接输出原字符串(0,0+d,0+2d....)下标的字符即可,此时我们就输出了第一列的字符;


那么这个公差 d 是多少呢?我们通过公差第一行的 0 和 6,发现它们之间的差值 d,就是 6 之前,0之后的总格子数:


如何计算这些格子数,进而确定 d 呢?


我们通过观察可以发现,d=numRows*2-2,那么最后一行也是同理;


而对于第二行,则是从第一列元素开始,+d 的元素所在列的前一列,多了一个元素:

并且 1+5 = d,第三行 2+4 = d,所以中间的行是两个两个元素进行递进;


设中间某一行第一列元素为 k:


处理细节问题


当 numRows = 1,要特别处理一下,否则会因为 d = 0 而写出死循环;


编写代码



题一定不要听一遍课就行了,要自己也过几遍,直到一次过,煮啵隔了一天再换一种方法刷错一堆,呜呜呜~~~


外观数列


题目解析



算法原理


解法: 模拟 + 双指针



要想通过 cAS( i-1 ) 求出 cAS(i),其实非常简单,我们只需要找 cAS(i) 相同部分,直到遍历完相同部分,就能得到 cAS ( i ) 对应的两个元素 [ 元素出现次数,元素 ];


模拟步骤


我们定义一个双指针,指向起始位置:

让 right 移动出相同部分(找边界):

cAS[right]==cAS[left] ?right++ : left = right;

当然,再移动 left 之前,要先记录相同部分的元素出现次数 [ right - left , cAS[left] ]


编写代码



细节问题


在绝对的力量面前,技巧输的一败涂地 (bushi)


数青蛙


题目描述



算法原理


从前往后遍历:


如果遍历到 r 字符,就需要看看 r 前面有没有青蛙叫出 c 字符:


同理,我们如果在继续扫描的过程中,扫描到 o 字符,就可以看看前面有没有 r 字符 ,并且这个 o 字符没有被其他的 o 字符挡住:


所以,我们在遍历的过程中,要时刻统计前面遍历过的字符出现的次数情况,所以我们需要借助一个哈希表,<字符种类,出现次数>;


解法:模拟 + 哈希表


我们来模拟一下遍历字符串时,哈希表的键值变化过程:


我们在遍历下一个字符时,发现是 r ,此时我们要减去一个 c ,才可以让 r 的 val++

如果有不合法的字符串,会让哈希表中某一个键值对的 val = -1,此时直接返回 -1 即可;

并且,要想知道到底有多少只青蛙,看 key = k 对应的 val 是不够的,因为可能是一只青蛙叫了很多声;


继续往后遍历:


遍历到 'o' 时,我们需要看看前面是否有 'r' :


其实本质就是如果遍历到 'o' ,就把前面 'r' 对应的 val 搬一个 1到 'o' 的 val:


具体操作就是前一个 val -1 ,后一个 val +1,但是我们模拟过程的时候,如果前面的key 对应 val =1,是可以直接把1 移动到下一个key 的 val 上的;


此时我们向后模拟,发现 c r o a k 完整出现一次,默认就是前面两只青蛙其中一只叫的 ;


既然要前面两只青蛙中的其中一只叫,那我们的模拟过程就是,把后面 key = k 对应的 val ,把这个 val 中的一个 1 搬回到 'c' 对应的 val(这一步很关键):


所以我们的具体操作,就是让 hash.getOrDefault (k,0)--,hash.getOrDefault (c,0)++,这样就可以保证求出来的青蛙数量是最少的:

最终 k 的值就能代表青蛙的个数;

这是完美模拟的情况,并且我们的这个实例最终结果也是需要两只青蛙,并且 k 前面的所有字符,对应的 val 都为 0 ;如果前面有键值对的 val != 0 ,就能说明这个字符串不合法;


当然,也有一些不需要遍历完所有字符串,就可以直接判定字符串不合法的情况:


当遍历到第二个 r 时,我们我们可以观察还没有更新的哈希表的情况:

此时我们发现在遍历第二个 r 时,c.val = 0,说明这个 r 是多余的字符,可以直接返回 -1;


总结:


编写代码


对于这题,已经给出蛙声是怎么叫的了,我们可以用大量的判断语句来解决这道题;


但是如果题目不给蛙声的字符串是什么样的,而只是把蛙声作为一个参数传进来,那么我们就不能再使用判断语句来解决这道题


无论蛙鸣的组成字符如何变化,或者不知道蛙鸣的具体字符组成,都可以使用的通用解法:


处理细节问题


(1) 两次 return -1 筛查的字符串类型不同


注意:

第一个 return -1,是在遍历到当前字符时,还没有要修改当前字符和前驱字符的 val,只是看一下前驱字符的 val 是否有值,可以移动一个 1 给当前字符的 val;如果无法移动,说明当前遍历字符是不合法的;


(2) 使用 哈希表+数组 的组合简化映射关系


在这个题,煮啵又学到了新的骚操作:

对于使用数组来模拟哈希表,在一些特定需求导致数组的下标紊乱的情况:

如本题,如果我们直接来创建两个数组来模拟哈希表,一个数组 nums1 放蛙声中的字符,另一个数组 nums2 放的是蛙声字符的次数,每一个字符在nums2 都会有对应下标,那么它们之间的映射关系是非常混乱而复杂;


此时我们可以定义一个哈希表,来绑定字符对应在数组中的下标 :

此时映射关系简单而易于理解,使用时就不会出错;


相关推荐
Joe_Wang52 分钟前
[leetcode]1786. 从第一个节点出发到最后一个节点的受限路径数(Dijkstra+记忆化搜索/dp)
算法·leetcode·图论
泽02024 分钟前
括号匹配问题--栈
数据结构
spssau10 分钟前
论文评价指标体系构建,AHP-熵值法组合赋权,11种权重计算方法汇总
人工智能·算法·机器学习
ChoSeitaku36 分钟前
NO.76十六届蓝桥杯备战|数据结构-单调栈|发射站|Largest Rectangle in a Histogram(C++)
数据结构·c++·蓝桥杯
烁3471 小时前
每日一题(小白)暴力娱乐篇20
java·开发语言·算法·排序算法·娱乐
写个博客1 小时前
代码随想录算法训练营第十三天
算法
满天星83035771 小时前
文件的操作
数据结构·c++·算法
heyCHEEMS1 小时前
01背包 Java
java·算法·深度优先
ChoSeitaku1 小时前
NO.81十六届蓝桥杯备战|数据结构-Trie树-字典树-前缀树|于是他错误的点名开始了|最大异或对 The XOR Largest Pair(C++)
数据结构·c++·蓝桥杯
雾月557 小时前
LeetCode 941 有效的山脉数组
java·开发语言·数据结构·算法·leetcode·职场和发展