目录
[Z 字形变换](#Z 字形变换)
注意: 模拟算法其实就是将题目描述的流程转换为代码,当根据题目流程转换后时间复杂度或者空间复杂度特别高时通常可以通过找规律来优化代码。
替换所有的问号

**思路:**遍历字符串,碰到 '?' 就进行替换,替换时从 a~z 尝试即可,只要不和前后的字符重复即可,但是需要考虑一些特殊情况,第一个字符如果是 '?' 替换时就不需要比前面的字符了,最后一个字符如果是 '?' 替换时就不需要比后面的字符了。
代码:
cpp
class Solution {
public:
string modifyString(string s) {
for(int i = 0; i < s.size(); i++){
if(s[i] == '?'){
for(int j = 0; j < 26; j++){
if(i - 1 >= 0)
if(s[i - 1] == 'a' + j)
continue;
if(i + 1 < s.size())
if(s[i + 1] == 'a' + j)
continue;
s[i] = 'a' + j;
break;
}
}
}
return s;
}
};
提莫攻击

**思路:**当两次攻击时间的差值 >= duration 时,说明第二次攻击是在第一次中毒状态结束后攻击的,此时第一次攻击的中毒时间按 duration 算;当两次攻击时间的差值 < duration 时,说明第二次攻击是在第一次中毒状态还没结束就攻击了,此时第一次攻击中毒的持续时间只需要按两次攻击的时间差算就行。以此类推,直到最后一次攻击,因为最后一次攻击持续时间一定是 duration,所以最后一次攻击不需要像前面那样比较,直接向结果中加 duration 即可。
代码:
cpp
class Solution {
public:
int findPoisonedDuration(vector<int>& timeSeries, int duration) {
int ret = 0;
for(int i = 0; i < timeSeries.size(); i++){
if(i == timeSeries.size() - 1 || timeSeries[i + 1] - timeSeries[i] >= duration)
ret += duration;
else
ret += timeSeries[i + 1] - timeSeries[i];
}
return ret;
}
};
Z 字形变换

思路: 这道题主要是找给定的行数和字符按照Z形排列后的下标之间的关系,然后根据这个关系,在遍历字符串的时候直接依次取出相应的字符拼接成结果字符串。具体如图:
代码:
cpp
class Solution
{
public:
string convert(string s, int numRows)
{
if(numRows == 1)
return s;
string ret;
int n = s.size();
int d = numRows * 2 - 2;
for(int i = 0; i < n; i += d)
{
ret += s[i];
}
for(int i = 1; i < numRows - 1; i++)
{
for(int k = i, j = d - k; k < n || j < n; k += d, j += d)
{
if(k < n)
ret += s[k];
if(j < n)
ret += s[j];
}
}
for(int i = numRows - 1; i < n; i += d)
{
ret += s[i];
}
return ret;
}
};
外观数列

**思路:**还是模拟的思想,直接将题目描述的过程转化成代码,另外还需要使用双指针来配合模拟的过程,方便确定连续的相同数字的区间。具体如图:

- left 指针指向的数字就是当前需要描述的数字。
- right - left 就是当前要描述的数字的个数。
- 当前区间描述完让 left 移动到 right 位置,继续找下一个区间即可。
代码:
cpp
class Solution {
public:
string countAndSay(int n) {
string ret = "1";
for(int i = 1; i < n; i++){
int left = 0;
int right = 0;
string tmp;
while(right < ret.size()){
while(ret[left] == ret[right])
right++;
tmp += (right - left) + '0';
tmp += ret[left];
left = right;
}
ret = tmp;
}
return ret;
}
};
数青蛙

**思路:**创建一个哈希表,存储 croak 中每个字符和对应出现次数的映射关系,遍历字符串,当前遍历的字符是 roak 中的任意一个时,找一下哈希表中当前字符的前驱字符是否在哈希表中存在,如果存在,说明有青蛙叫了上一个字符,让这个青蛙接下来叫这个字符,即前面的字符个数自减 1,当前字符数量自加 1,如果前面字符不存在,说明字符串不是有效组合,因为题目要求字符串必须由若干有效的croak组成,而当前字符的前驱字符不存在,说明当前遍历的这个 croak 是不完整的,返回 -1。如果当前遍历的字符是 c,找 k 对应次数是否大于 0,如果大于零,让 k 自减 1,c 对应个数自加 1,否则只让 c 的个数自加 1即可,因为题目要求最少的青蛙,叫到 k 的青蛙已经叫完了完整的一声,所以当遇到 c 时,如果有青蛙叫完了,可以让叫完的青蛙去继续叫。
代码:
cpp
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];
}
};