文章目录
本篇是优选算法之模拟算法
,是一种依据问题给定的规则与逻辑,按步骤细致地重现事件的发展流程,进而获取最终结果的算法
1.概念解析
🚩什么是模拟算法?
简单来说就是
照葫芦画瓢
,题目说啥就照着写代码就行,所以很考察代码能力
2.替换所有的问号
✏️题目描述:
✏️示例:
传送门: 替换所有的问号
题解:
这题很简单,显然是先遍历一遍数组
,然后在遍历的时候,如果遇到?
就替换为与该位置前后都不相同
的数,所以也要对要替换的数遍历一遍
,直到发现合适的数
💻细节问题:
注意边界情况,
没有两个相邻的数
💻代码实现:
cpp
#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
string modifyString(string s)
{
for (int i = 0; i < s.size(); ++i)
{
if (s[i] == '?')
{
for (char ch = 'a'; ch <= 'z'; ++ch)
{
if ((i == 0 || ch != s[i - 1]) && (i == s.size() - 1 || ch != s[i + 1]))
{
s[i] = ch;
break;
}
}
}
}
return s;
}
};
3.提莫攻击
✏️题目描述:
✏️示例:
传送门: 提莫攻击
题解:
做模拟算法题的精髓就是要画图
,只有图画明白了,才能找到规律,而不是凭空想象
所以根据规律发现相邻两数之差
是突破点,如果相邻两数之差大于duration
,那么不用
重置中毒时间;如果相邻两数之差小于duration
,那么要
重置中毒时间
💻代码实现:
cpp
#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
int findPoisonedDuration(vector<int>& timeSeries, int duration)
{
int ret = 0;
for (int i = 1; i < timeSeries.size(); ++i)
{
int x = timeSeries[i] - timeSeries[i - 1];
if (x >= duration)
{
ret += duration;
}
else
{
ret += x;
}
}
return ret + duration;
}
};
4.Z字形变换
✏️题目描述:
✏️示例:
传送门: Z字形变换
题解:
本题的意思是要你根据题目给出的Z字形字符串
,将其从上到下
,从左至右
的方式以一个新字符串
的方式返回,显然这是一个找规律性极强
的题目,所以画图!画图!画图!
💻第一步:
首先我们将每个数的索引按照Z字形填入
,我们是从第一行
开始录入,所以可以发现第一行
和最后一行
都是一个等差数列
,然后发现规律,公差是0~6之间元素的个数
,所以得出d=2n-2
💻第二步:
接下来处理
中间的行数
,发现如图的1 ~ 7 ~ 13
,5 ~ 11 ~ 17
也是以6为公差
,只不过是两个数两个数的差
,所以得出规律(k,d-k)
💻细节问题:
注意for循环的判断条件为
i < n || j < k
,而不是&&
,因为当一个越界之后,另一个可能还没越界判断完
💻代码实现:
cpp
#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
string convert(string s, int numRows)
{
if (numRows == 1)
{
return s;
}
string ret;
int n = s.size();
int d = 2 * numRows - 2;
for (int i = 0; i < n; i += d)
{
ret += s[i];
}
for (int k = 1; k < numRows - 1; ++k)
{
for (int i = k, j = d - k; i < n || j < k; i += d, j += d)
{
if (i < n)
{
ret += s[i];
}
if (j < n)
{
ret += s[j];
}
}
}
for (int i = numRows - 1; i < n; i += d)
{
ret += s[i];
}
return ret;
}
};
5.外观序列
✏️题目描述:
✏️示例:
传送门: 外观序列
题解:
其实题目意思就是对上一行的序列进行解释
,比如第三行为21
,那么第四行
的意思就是一个2一个1
💻细节问题:
所以很明显这题要用双指针
解决,因为要持续向后查找有几个相同的,直到不同为止
left
和right
都从0
开始,然后right
向后移动,直到该位置和前一个位置不相同,然后记录长度和数字,直接让left=right
,开始下一段
💻代码实现:
cpp
#include <iostream>
#include <string>
using namespace std;
class Solution
{
public:
string countAndSay(int n)
{
string ret = "1";
for (int i = 1; i < n;++i)
{
string tmp;
int len = ret.size();
for (int left = 0, right = 0; right < len; )
{
while (ret[left] == ret[right])
{
right++;
}
tmp += to_string(right - left) + ret[left];
left = right;
}
ret = tmp;
}
return ret;
}
};
6.数青蛙
✏️题目描述:
✏️示例:
传送门: 数青蛙
题解:
要理解本题题意,就是一只青蛙要完整叫完croak
,如果一只青蛙没叫完,那么就需要另一只青蛙来发出叫声
💻第一步:
首先,以如图所示为例子,第一个c
,计入该位置为1
,接下来到r
,该位置变为1
,上一个位置变为0
,可以理解为一个1一直在移动
,表示这个叫声遍历到哪里了,直到遍历到k为止
💻第二步:
注意在遍历过程中注意要像图中分类,对于字符c
,若k位置不为0
,那么表示有青蛙闲置,那么之前叫完的青蛙就可以再次叫
;对于其他字符
,要找该字符的前一个字符符不符合croak的顺序和字符
,若不符合字符
,直接返回-1
💻细节问题:
注意处理完所有字符时,要判断一下
是否还有没叫完的叫声
💻代码实现:
cpp
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
using namespace std;
class Solution
{
public:
int minNumberOfFrogs(string croakOfFrogs)
{
string t = "croak";
int n = t.size();
vector<int> hash(n);
unordered_map<char, int> index;
for (int i = 0; i < n; ++i)
{
index[t[i]] = i;
}
for (auto ch : croakOfFrogs)
{
if (ch == 'c')
{
if (hash[n - 1] != 0)
{
hash[n - 1]--;
}
hash[0]++;
}
else
{
int i = index[ch];
if (hash[i - 1] == 0)
{
return -1;
}
hash[i - 1]--;
hash[i]++;
}
}
for (int i = 0; i < n - 1; ++i)
{
if (hash[i] != 0)
{
return -1;
}
}
return hash[n - 1];
}
};