文章目录
- 一、模拟类算法题的特征
- 二、习题解析
-
- [1. Z 字形变换](#1. Z 字形变换)
- [2. 外观数列](#2. 外观数列)
- [3. 数青蛙(hard)](#3. 数青蛙(hard))
一、模拟类算法题的特征
-
题目描述直观
模拟题通常直接描述一个现实场景 或 规则系统,要求用代码还原过程。 例如棋类游戏规则、物理运动轨迹、自动机行为等。 -
流程明确
题目会给出明确的步骤或状态转移规则,例如:
(1)每一步如何更新数据
(2)终止条件是什么
(3)输入输出格式固定
-
边界条件多
需要处理大量特殊情况,例如:
(1)数值溢出
(2)初始状态和终止状态
(3)无效输入的处理
二、习题解析
1. Z 字形变换
- 题目描述:
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
bash
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
请你实现这个将字符串进行指定行数变换的函数:
bash
string convert(string s, int numRows);
示例 1:
输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
示例 2:输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
bashP I N A L S I G Y A H R P I
示例 3:
- 输入:s = "A", numRows = 1
- 输出:"A"

- 示例演示:
cpp
class Solution {
public:
string convert(string s, int numRows) {
if(numRows == 1) // 特殊情况处理
return s;
int cvt = 2*numRows - 2;
string tmp;
for(int row = 0;row < numRows;row++)
{
if((row == 0) || (row == numRows - 1)) // 处理第一行 和 末尾行的情况
{
for(int i = row;i < s.size();i += cvt)
{
tmp += s[i];
}
}
else // 处理中间行的情况
{
if(row < s.size() && numRows > 2)
tmp += s[row];
int i = cvt;
for(;i < s.size();i += cvt)
{
tmp += s[i-row];
if(i + row < s.size())
tmp += s[i+row];
}
if(i-row < s.size())
tmp += s[i-row];
}
}
return tmp;
}
};
2. 外观数列
- 题目描述:
「外观数列」是一个数位字符串序列,由递归公式定义:
- countAndSay(1) = "1"
- countAndSay(n) 是 countAndSay(n-1) 的行程长度编码。
行程长度编码(RLE)是一种字符串压缩方法,其工作原理是通过将连续相同字符(重复两次或更多次)替换为字符重复次数(运行长度)和字符的串联。
例如,要压缩字符串 "3322251" ,我们将 "33" 用 "23" 替换,将 "222" 用 "32" 替换,将 "5" 用 "15" 替换并将 "1" 用 "11" 替换。因此压缩后字符串变为 "23321511"。
给定一个整数 n ,返回 外观数列 的第 n 个元素。
示例 1:
输入:n = 4
输出:"1211"
解释:
countAndSay(1) = "1"
countAndSay(2) = "1" 的行程长度编码 = "11"
countAndSay(3) = "11" 的行程长度编码 = "21"
countAndSay(4) = "21" 的行程长度编码 = "1211"
示例 2:输入:n = 1
输出:"1"
解释: 这是基本情况。
提示:
- 1 <= n <= 30

- 示例演示:
cpp
class Solution {
public:
string countAndSay(int n)
{
string count_str = "1";
while(n > 1) // 迭代 n 轮生成第 n 个外观数列
{
string temp;
// 使用双指针统计连续且相同的字符个数
int left = 0, right = 1;
while(right < count_str.size())
{
if(count_str[left] != count_str[right])
{
temp += right - left + '0';
temp += count_str[left];
left = right;
}
right++;
}
temp += right - left + '0';
temp += count_str[left];
count_str = temp;
n--;
}
return count_str;
}
};
3. 数青蛙(hard)
- 题目描述:
给你一个字符串 croakOfFrogs,它表示不同青蛙发出的蛙鸣声(字符串 "croak" )的组合。由于同一时间可以有多只青蛙呱呱作响,所以 croakOfFrogs 中会混合多个 "croak" 。
请你返回模拟字符串中所有蛙鸣所需不同青蛙的最少数目。
要想发出蛙鸣 "croak",青蛙必须 依序 输出 'c', 'r', 'o', 'a', 'k' 这 5 个字母。如果没有输出全部五个字母,那么它就不会发出声音。如果字符串 croakOfFrogs 不是由若干有效的 "croak" 字符混合而成,请返回 -1 。
示例 1:
输入:croakOfFrogs = "croakcroak"
输出:1
解释:一只青蛙 "呱呱" 两次
示例 2:输入:croakOfFrogs = "crcoakroak"
输出:2
解释:最少需要两只青蛙,"呱呱" 声用黄色标注 第一只青蛙 "crcoakroak" 第二只青蛙 "crcoakroak"
示例 3:输入:croakOfFrogs = "croakcrook"
输出:-1
解释:给出的字符串不是 "croak" 的有效组合。
提示:1 <= croakOfFrogs.length <= 10^5
字符串中的字符只有 'c', 'r', 'o', 'a' 或者 'k'

- 代码演示:
cpp
class Solution {
public:
int minNumberOfFrogs(string croakOfFrogs)
{
if(croakOfFrogs.size() % 5 != 0)
return -1;
unordered_map<char, int> hash; // 创建哈希表
hash['c'] = 0;
hash['r'] = 0;
hash['o'] = 0;
hash['a'] = 0;
hash['k'] = 0;
for(auto it : croakOfFrogs)
{
if(it == 'c')
{
if(hash['k'] > 0)
hash['k']--;
hash['c'] += 1;
}
if(it == 'r')
{
if(hash['c'] > 0)
{
hash['c']--;
hash['r'] += 1;
}
else
return -1;
}
if(it == 'o')
{
if(hash['r'] > 0)
{
hash['r']--;
hash['o'] += 1;
}
else
return -1;
}
if(it == 'a')
{
if(hash['o'] > 0)
{
hash['o']--;
hash['a'] += 1;
}
else
return -1;
}
if(it == 'k')
{
if(hash['a'] > 0)
{
hash['a']--;
hash['k'] += 1;
}
else
return -1;
}
}
for(auto it : "croa") // 检查哈希表其它位置是否大于0
{
if(hash[it] > 0)
return -1;
}
return hash['k']; // 返回哈希表中 'k' 字符位置的个数
}
};