我爱学算法之—— 模拟(上)
一、替换所有的问号
题目解析
替换所有的问号,给定一个字符串
s
(其中仅包含?
和小写字母);我们要将s
中所有的?
替换成小写字母,并且还要保证最终的字符串中,不包含连续重复的字符(相邻的字符都不相同)
算法思路
对于这道题,总体来说还是非常简单的;只需要按照题目要求遍历s
,在遍历到?
时将其替换成小写字母(与前一个字符和后一个字符都不相同)。
注意:
当第一个字符为
?
时,是不存在前一个字符的,只需要保证和后一个字母不相同即可;当最后一个字符为
?
时,是不存在后一个字符的,只需要保证和前一个字符不相同即可。
代码实现
class Solution {
public:string modifyString(string s) {int n = s.size();for (int i = 0; i < n; i++) {if (s[i] == '?') {for (char ch = 'a'; ch <= 'z'; ch++) {if ((i == 0 || ch != s[i - 1]) &&(i == n - 1 || ch != s[i + 1])) {s[i] = ch;}}}}return s;}
};
二、提莫攻击
题目解析
题目:提莫每次攻击会使艾希中毒duration
秒,如果提莫攻击艾希时,艾希处于中毒状态,则时间会重置(不会叠加)。
先给定一个非递减的数组timeSeries
,表示提莫攻击艾希的时间(提莫会在timeSeries[i]
秒时攻击艾希)。
这道题目要求我们计算出,艾希处于中毒状态的总时间(秒)。
算法思路
对于这道题,思路也是非常简单,按照题目要求遍历timeSeries
数组,统计艾希处于中毒状态的时间即可。
通过读题,我们会发现,提莫的最后一次攻击一定会导致艾希处于中毒状态duration
秒;
而其他每一次攻击,导致艾希进入中毒状态的时间都和前一次攻击有关。
提莫第
i+1
次攻击攻击艾希时,无非就只有三种情况:
- 当前艾希处于中毒状态:重置中毒时间,第
i
次攻击导致艾希处于中毒状态时间timeSeries[i+1] - timeSeries[i]
。- 艾希刚好中毒状态解除:艾希进入中毒状态,第
i
次攻击导致艾希处于中毒状态时间duration
(timeSeries[i+] - timeSeries[i]
);- 艾希没有处于中毒状态:艾希进入中毒状态,第
i
次攻击导致艾希处于中毒状态时间duration
。所以,提莫第
i
次攻击,导致艾希的中毒时间:time = min(duration,timeSeries[i+1] - timeSeries[i]
。
所以,只需遍历[0 , n-2]
次攻击,算出艾希中毒的时间再加上duration
(第n-1
次攻击必定导致艾希中毒duration
秒)。
代码实现
class Solution {
public:int findPoisonedDuration(vector<int>& timeSeries, int duration) {int ret = 0;int n = timeSeries.size();for (int i = 0; i < n - 1; i++) {int x = timeSeries[i + 1] - timeSeries[i];ret += min(duration, x);}ret += duration;return ret;}
};
三、Z 字形变换
题目解析
这道题,给定一个字符串s
,这里要将字符串s
从上往下,从左往右进行N
字形排列;然后再从左往右逐行读取,产生一个新的字符串然后返回。
算法思路
对于这道题,思路就很清楚了:模拟整个过程
思路一:
排列
N
字形:将字符串
s
排列成N
字形,这里可以使用一个二维数组,将字符串s
排列成如上图所示N
字形,再遍历整个数组,形成结果字符串。
但是,这样做时间复杂度和空间复杂度都是
O(n * numRows)
;并且遍历的过程中填写这个二维数组也是非常的麻烦。这里,我们就可以使用
C++
中的string
进行小小的空间和时间的优化。优化一 :在填写二维数组的过程中,只需要关注要将字符放在哪一行中(哪一个字符串中)。
优化二 :在形成最终结果字符串时,只需要将字符串按行拼接在一起即可(字符串中不存在空格)。
思路二:
思路二也就是对模拟思路的一个优化:
在模拟过程中,直接将字符放到指定位置可能看不出来,这里使用下标代替字符:
可以发现,第一行的字符下标呈现出一个等差数列,公差为
6
(也就是这两列中,下面所有行的字符总数)而最后一行的字符下标,也呈现出一个等差数列,公差为
6
。而中间的每一行,大眼一看,不是等差数列啊;仔细观察我们可以发现:中间的每一行,可以看成两个等差数列;其中一个数列首项为
x
,另一个数列首项就为6-x
(也就是d - x
)。公差:显而易见,公差
d = 2n - 2
所以,我们只需要先遍历第一行、再遍历中间的每一行、最后遍历最后一行(将每一行下标对应的字符,拼接成一个字符串返回即可)。
遍历的过程中,注意不要越界。
特殊情况:如果
numRows
等于1
,此时计算出公差为0
,并且numRows
等于1
时,无需转换,s
就是最终结果。这里就特殊处理,如果
numRows
等于1
,就直接返回s
。
代码实现
思路一:
class Solution {
public:string convert(string s, int numRows) {if (numRows == 1)return s;int i = 0;int flag = 0; // flag 0 -> i++ 1 -> i--vector<string> vs(numRows, string());for (auto& e : s) {if (i == 0)flag = 0;else if (i == numRows - 1)flag = 1;vs[i] += e;if (flag == 0)i++;elsei--;}string ret;for (auto& e : vs) {ret += e;}return ret;}
};
思路二:
class Solution {
public:string convert(string s, int numRows) {if (numRows == 1)return s;int d = 2 * numRows - 2;int n = s.size();string ret;// 第一行for (int i = 0; i < n; i += d)ret.push_back(s[i]);// 中间行for (int k = 1; k < numRows - 1; k++) {for (int i = k, j = d - k; i < n || j < n; i += d, j += d) {if (i < n)ret.push_back(s[i]);if (j < n)ret.push_back(s[j]);}}// 最后一行for (int i = numRows - 1; i < n; i += d)ret.push_back(s[i]);return ret;}
};
本篇文章到这里就结束了,感谢支持
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws