算法日记:模拟(提莫攻击,替换所有的问号,Z字形变换,外观数列,数青蛙)

🎬 胖咕噜的稞达鸭个人主页
🔥 个人专栏 : 《数据结构《C++初阶高阶》
《Linux系统学习》
《算法日记》

⛺️技术的杠杆,撬动整个世界!


模拟:这个专题最重要的就是理解题意。借助纸和笔实现题目中的过程。找出规律即可。

替换所有的问号

[1576. 替换所有的问号 - 力扣(LeetCode)](https://leetcode.cn/problems/replace-all-s-to-avoid-consecutive-repeating-characters/submissions/686036885/)(替换所有的问号)

题目解析:

字符串中有问号?就替换成26个字母其一,最后返回的字符串中不允许出现重复的字符。

算法原理:

先遍历一遍字符串找出问号,接着替换,用26个英文字母一个一个替换,看是否满足这个空,不能跟左边的字母相同,也不能跟右边的字母相同。

如果si问号空有可能在:

要么在字符串的第一个位置,要么是跟前面一个位置的字符不相同;

或者是最后一个位置,要么就是跟后一个位置的字符不相同;

(这两个条件要同时满足)

同时满足就插入。

cpp 复制代码
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;
                        break;
                    }
                }
            }
        }
        return s;
    }
};

提莫攻击

题目解析:给定一个时间区间 [t, t + duration - 1](含 tt + duration - 1)处于中毒状态。有一个不变的量duration,作为每一次>中毒的持续时长,当第一次中毒之后,还没有恢复(没有超过中毒的持续时长)再次中毒,就会从第二次中毒的时刻重新往后记单次中毒时长。

求解中毒状态的总秒数。

算法原理:

cpp 复制代码
//下面给定一个时间区间[1,19] duration = 3
1   4   5   8   9   15   19

//第1秒中毒: 中毒区间【1,3】
//第4秒中毒: 中毒区间[4, 6] ---> 由于第5秒又中毒:中毒区间[5,7]
//第8秒中毒: 中毒区间[8,10] ---> 由于第9秒又中毒:中毒区间[9,11]
//第15秒中毒:中毒区间[15,17]
//第19秒中毒:中毒区间[19,21]

先定义一个ret来收集最后的中毒秒数。

遍历数组:

从索引为1的位置开始,如果第一次和第二次中毒的间隔时长大于等于duration,直接计入ret中;

如果第一次和第二次中毒的间隔时长小于duration, 那么该计入第一次中毒时长的只能是两次间隔差。

cpp 复制代码
class Solution {
public:
    int findPoisonedDuration(vector<int>& timeSeries, int duration) {
        int ret = 0;//返回最后的秒数
        for(int i = 1;i < timeSeries.size();i++)
        {
            int x = timeSeries[i] - timeSeries[i-1];
            if(x >= duration) ret += duration;
            else ret += x;
        }
        return ret + duration;
    }
};

Z字形变换

解法一 :模拟实现(自己画图),将数组中的数字放入一个矩阵中实现;放入之后一行一行遍历,一定会超过时间限制的。
解法二 :找规律

让一个字符的索引值计入矩阵,这样就可以很清楚找出规律:

行数记为n; 公差d记为2n -2。(第一行中前两个数字的差值为 2n - 2);两列的数字加起来减去两个空格。

所以

复制代码
第一行规律:0 -> 0+d -> 0+2d ->... 0+kd;
第二行规律: (i, d-i) -> (i+d, d-i+d)
最后一行规律: n-1 -> n-1+d -> n-1+2d ->... n-1+kd;

特殊情况:当只需要1行,直接返回当前数组。

cpp 复制代码
class Solution {
public:
    string convert(string s, int numRows) {
        //处理边界情况:当只需要返回一行
        if( numRows == 1)
        return s;
  
        string ret;//用来记录最终的
        int d = 2 * numRows -2,n = s.size();
        //1.先处理第一行
        for(int i = 0;i < n;i+=d)ret+=s[i];
        
        //2.接着处理中间行
        for(int k = 1;k < numRows-1;k ++)//枚举中间的每一行
        {
            for(int i = k,j = d-k; i<n || j< n;i += d,j += d)//i和j其中某一个越界
            {
                if(i < n)ret += s[i];//i没有越界就进入去
                if(j < n)ret += s[j];//j同理
            }
        }

        //3.再处理最后一行
        for(int i = numRows-1;i<n;i+= d)ret+=s[i];
        return ret;
    }
};

外观数列

算法原理:

模拟+双指针

cpp 复制代码
class Solution {
public:
    string countAndSay(int n) {
        string s = "1";//将s初始化为1
        if(n == 1)return s;
        for(int i = 1;i < n;i ++)//解释n-1次s即可
        {
            string tmp;
            int len = s.size();
            for(int left = 0,right = 0;right < len;)
            {
                while(right < len && s[right] == s[left])//如果遇到重复的字符,而且right向后移动的时候不会越界
                right++;
                tmp += to_string(right - left) + s[left];//个数to_string()多少个s[left]
                left = right;
            }
            s = tmp;
        }
        return s;
    }
};

数青蛙

算法原理:

对于r o a k的字符:找一下前驱字符,是否在哈希表中存在:

  1. 存在:前驱个数--,当前字符++;
  2. 不存在:返回-1

对于c来说,找最后一个字符,是否在哈希表中存在:

  1. 存在:最后一个字符--,当前字符++;
  2. 不存在:当前字符++

算法思路:

  1. 每个青蛙发出"croak"声音时,可以看作是状态转移
  1. 遇到'c': 可能是新青蛙开始叫,也可能是青蛙完成一轮后重新开始
  1. 遇到其他字符: 必须检查前一个字符是否存在(确保顺序正确)
  1. 最终:除了'k',其他字符计数应为0,'k'的计数就是最小青蛙数
cpp 复制代码
class Solution {
public:
    int minNumberOfFrogs(string croakOfFrogs) {
        string t = "croak";
        int n = t.size();
        vector<int>hash(n);//用数组来模拟哈希表
        unordered_map<char,int>index;//[x,x字符对应的下标]
        for(int i = 0; i < n;i++)index[t[i]] = i;
        for(auto ch : croakOfFrogs)
        {
            if(ch == 'c')
            {
                //如果k在哈希表中存在
                if(hash[n - 1] != 0)hash[n-1]--;//此时这种情况就是让同一个青蛙,叫完k之后又从当前c开始叫
                hash[0]++;//当k不在哈希表中,另一个青蛙,不管是不是同一个青蛙,字符c都要向后遍历
            }
            else //遍历到'r''o''a''k'字符,看前驱字符在否,不在就说明不能发出声音;在就继续遍历
            {
                int i = index[ch];
                if(hash[i-1] == 0)return -1;//前驱字符不存在
                else
                hash[i-1]--;//前驱存在,此时让前驱--,此时的字符++,这样统计到最后的k,就可以判断出需要几个青蛙来叫这一个字符串了
                hash[i]++;
            }
        }
        for(int i = 0;i < n - 1;i++)
            if(hash[i] != 0)return -1;//如果字符串中某一个字符没有被后驱字符修改为hash[i] == 0 说明残缺不全,青蛙叫声不全
        return hash[n-1];
    }
};
相关推荐
想吃火锅100513 小时前
【leetcode】14.最长公共前缀js
算法·leetcode·职场和发展
云絮.14 小时前
数据库操作
数据库·mysql·算法·oracle
小林ixn14 小时前
LeetCode 206. 反转链表(迭代 + 递归详解)
算法·leetcode·链表
凡人叶枫15 小时前
Effective C++ 条款17:以独立语句将 newed 对象置入智能指针
java·linux·开发语言·c++·算法
菜鸟‍16 小时前
LeetCode 1 27 和 704 || 两数之和 移除元素 二分查找
算法·leetcode·职场和发展
退休倒计时17 小时前
【每日一题】LeetCode 142. 环形链表 II TypeScript
算法·leetcode·链表·typescript
popcorn_min18 小时前
Digits 手写数字识别:随机森林多分类 + 像素级特征热力图
算法·随机森林·分类
liulilittle18 小时前
拥塞控制:排水终止的两种决策:OR 与 AND
网络·tcp/ip·计算机网络·算法·信息与通信·tcp·通信
weixin_3077791319 小时前
从脚本执行到智能体协作:AI辅助测试能力的范式重构
运维·开发语言·人工智能·算法·测试用例
量化君也19 小时前
从回测到全自动实盘交易,全天候策略需要经历哪些改造?
大数据·人工智能·python·算法·金融