算法之模拟

 前言

模拟算法通俗地来解释就是 "照葫芦画瓢", 通常这类题的题目中就说明了这道题应该怎么做, 要做的就是把题目的意思转化为代码, 这类题的特点是思路比较简单, 考查的是代码能力.

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;
            else
                ret += 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']++;
                }
                else
                    return -1;
            }
            else if(croakOfFrogs[i] == 'o')
            {
                if(hash['r'])
                {
                    hash['r']--;
                    hash['o']++;
                }
                else
                    return -1;
            }
            else if(croakOfFrogs[i] == 'a')
            {
                if(hash['o'])
                {
                    hash['o']--;
                    hash['a']++;
                }
                else
                    return -1;
            }
            else if(croakOfFrogs[i] == 'k')
            {
                if(hash['a'])
                {
                    hash['a']--;
                    hash['k']++;
                }
                else
                    return -1;
            }
            else
                return -1;
        }

        if(hash['c'] || hash['r'] || hash['o'] || hash['a'])
            return -1;
        else
            return 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']];
    }
};

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值