前言
模拟算法通俗地来解释就是 "照葫芦画瓢", 通常这类题的题目中就说明了这道题应该怎么做, 要做的就是把题目的意思转化为代码, 这类题的特点是思路比较简单, 考查的是代码能力.
1. 模拟算法流程, 最好在演草纸上过一遍流程, 凭空想象可能会忽略一些细节
2. 把流程转化为代码
题目1: 替换所有的问号(easy)
从前往后遍历字符串, 遇到 '?' 就替换为 a~z 中符合题意的字母, 也就是每次替换需要和前后的字符进行比较判断是否符合题意, 注意开头和结尾位置的字母判断.
class Solution {
public:string modifyString(string s) {for(int i = 0; i < s.length(); i++){if(s[i] == '?'){char c = 'a';s[i] = c;//默认都替换为a, 如果和前面字母相等或者和后面字母相等就换字母while((i>=1 && s[i] == s[i-1]) || (i <= s.length()-1 && s[i] == s[i+1]))s[i] = ++c;}}return s;}
};
题目2: 提莫攻击(easy)
此题问题关键在于判断 相邻两次攻击的时间间隔 和 duration 的大小关系:
1. 如果是最后一次攻击, 直接加上duration即可.
2. 普通情况需要单独判断, 如果时间间隔>=duration, 直接+=duratio; 如果时间间隔<duration, += 时间间隔.
class Solution {
public:int findPoisonedDuration(vector<int>& timeSeries, int duration) {int ret = 0;int n = timeSeries.size();for(int i = 0; i < n; i++){if(i == n-1){ret += duration;break;}int gap = timeSeries[i+1] - timeSeries[i];if(gap >= duration)ret += duration;elseret += gap; }return ret;}
};
题目3: N 字形变换(medium)
可以通过找规律来优化, 第1行和第n-1行发现每两个元素间距是固定的, 设为d, d=2*n-2
第k行则多了两个元素, 下标分别为 k 和 d-k, 每两个元素间距依然是d:
class Solution {
public:string convert(string s, int numRows) {vector<vector<char>> arr(numRows);int d = 2*numRows-2;int n = s.size();//n=1的时候, d=0, 直接返回原字符串即可, 否则死循环if(d == 0)return s;for(int i = 0; i < numRows; i++){//处理第一行if(i == 0)for(int j = i; j < n; j+=d)arr[i].push_back(s[j]);//处理第k行else if(i == numRows-1)for(int j = i; j < n; j+=d)arr[i].push_back(s[j]);//处理第k行else{for(int j = i, k = d-j; j < n; j+=d,k+=d){arr[i].push_back(s[j]);if(k < n) arr[i].push_back(s[k]);}}}string ret;for(const auto& v:arr)for(const auto& c:v)ret+=c;return ret;}
};
题目4: 外观数列
利用双指针记录 被描述字符 和 它的长度, 然后依次描述即可:
class Solution {
public:string countAndSay(int n) {string ret = "1";//描述n-1即可for(int i = 1; i < n; i++){string tmp;int n = ret.size();int left = 0, right = 0;while(right < n){while(right < n && ret[right] == ret[left])right++;tmp += to_string(right-left) + ret[left];//添加描述的字符left = right;//更新left} ret = tmp;}return ret;}
};
题目5: 数青蛙
从前往后遍历crokaOFFrogs字符串, 如果遇到 'c' 说明有一个青蛙开始叫, 如果遇到 'r' 只需去查找前面的字符串中是否有 'c', 所以可以用哈希表来存储字符出现的次数, 也就是青蛙的个数, 遍历到 'r' 的时候, c字符出现个数>=1, 说明有青蛙可以叫出来'r', hash['c']--, hash['r']++, 代表有一个青蛙叫到 'r', 其它字符也是同理. 叫到'k'说明青蛙叫完了.
注意:
1. 我们寻找的是最小青蛙的个数, 如果hash['k']>=1, 说明有青蛙叫完了, 再遇到'c'的时候可以让叫完的青蛙去叫, 所以hash['k']--, hash['c']++
2. 如果字符串遍历结束k之前的字符有>=1的情况, 说明有青蛙没叫完, 返回-1
if else 写法:
class Solution {
public:int minNumberOfFrogs(string croakOfFrogs) {unordered_map<char,int> hash;int n = croakOfFrogs.size();for(int i = 0; i < n; i++){if(croakOfFrogs[i] == 'c'){if(hash['k'])hash['k']--;hash['c']++;}else if(croakOfFrogs[i] == 'r'){if(hash['c']){hash['c']--;hash['r']++;}elsereturn -1;}else if(croakOfFrogs[i] == 'o'){if(hash['r']){hash['r']--;hash['o']++;}elsereturn -1;}else if(croakOfFrogs[i] == 'a'){if(hash['o']){hash['o']--;hash['a']++;}elsereturn -1;}else if(croakOfFrogs[i] == 'k'){if(hash['a']){hash['a']--;hash['k']++;}elsereturn -1;}elsereturn -1;}if(hash['c'] || hash['r'] || hash['o'] || hash['a'])return -1;elsereturn hash['k'];}
};
哈希映射写法:
class Solution {
public:int minNumberOfFrogs(string croakOfFrogs) {string voice = "croak";int n = voice.size();vector<int> hash(n);//用数组作为哈希表//建立<字符,下标>的哈希映射unordered_map<char, int> index;for(int i = 0; i < voice.size(); i++)index[voice[i]] = i;for(int i = 0; i < croakOfFrogs.size(); i++){if(croakOfFrogs[i] == 'c'){if(hash[index['k']] != 0) hash[index['k']]--;//挑出一只叫完的青蛙去叫hash[index['c']]++;}else{int x = index[croakOfFrogs[i]];//检查叫声是否合法if(hash[x-1] == 0)return -1;//正常else{hash[x-1]--;hash[x]++;}}}//检查是否有未叫完的青蛙for(int i = 0; i < n-1; i++){if(hash[i] != 0)return -1;}//k的个数即是最少青蛙的个数return hash[index['k']];}
};