硅基计划4.0 算法 模拟

模拟说白了就是根据题目要求完成代码就好,但是重点在于代码设计和编写上
一、替换所有的问号
题目链接
这一题的意思其实很简单,只需要保证替换的字符和其前后两个位置不一样就好了
因此我们可以尝试放入字母a到z,然后检查其前后是否都不同就好
对于边界情况,如果?处于最前面和最后面,我们仅需要判断前面或后面即可
class Solution {
public String modifyString(String s) {
char [] ch = s.toCharArray();
int length = ch.length;
for(int i = 0;i<length;i++){
if(ch[i] == '?'){
//遍历字母
for(char chs = 'a';chs<'z';chs++){
//这里判断非常巧妙,考虑到了边界情况
//比如如果i==0,那么chs != ch[i-1]代码就不会执行
//就会去执行后半部分的chs != ch[i+1]代码
if((i == 0 || chs != ch[i-1]) && (i == length-1 || chs != ch[i+1])){
ch[i] = chs;
break;
}
}
}
}
return String.valueOf(ch);
}
}
二、提莫攻击
题目链接
比如1 2 3 4总共四秒,如果中毒时间duration = 2
则我在第一秒进行攻击,中毒会在第二秒后(第三秒开始的时候)结束
但是我在第二秒又进行攻击,时间会重置,中毒会在第三秒后(第四秒开始的时候)才结束
题目示例中[1,4],duration = 2,表示会在第一秒和第四秒进行攻击,中毒时间持续两秒
在第一秒受到攻击,中毒会持续到第三秒开始时(第三秒开始时中毒效果结束)
第四秒开始时,又受到攻击,中毒会持续到第六秒开始时(第六秒开始时中毒效果结束)
题目示例中[1,2],duration = 2,表示会在第一秒和第二秒进行攻击,中毒时间持续两秒
在第一秒受到攻击,本应该在第三秒开始时结束,但是第二秒又受到攻击,时间重置
中毒会持续到第四秒开始时结束(第四秒开始时中毒效果结束)
好,我们通过上面示例可以发现,如果两次攻击间隔是>=duration时间的
会导致第一次的攻击的中毒效果不会影响到第二次攻击的中毒效果,也就是说第一次攻击可以把中毒buff吃满
反而如果两次攻击间隔是<duration的,会导致第一次攻击的中毒效果持续时间被第二次攻击重置
好,我们来举个例子[1,3,6,7],duration = 2
第一秒受到攻击,第三秒再次受到攻击,和第一次时间差值=duration,因此第一次攻击吃满中毒持续时间
第六秒再次受到攻击,和第二次时间差值>duration,因此上一次攻击依然吃满中毒持续时间
第七秒再次受到攻击,和第二次时间差值<duration,因此上一次攻击并不能吃满中度持续时间,中毒时间是7-6=1
此后再没受到攻击,但是不要忘记第七秒受到了攻击,因此最后还要吃满一次中毒持续时间
class Solution {
public int findPoisonedDuration(int[] timeSeries, int duration) {
//因为最后一次的后面没有再攻击了
//因此把最后一次的中毒时间提前加上
int ret = duration;
for(int i = 1;i<timeSeries.length;i++){
int distance = timeSeries[i]-timeSeries[i-1];
if(distance >= duration){
ret += duration;
}else{
ret += distance;
}
}
return ret;
}
}
三、N字型变换
题目链接
这道题就是说根据一个字符串创建一个N字型矩阵,再逐行读取就好
比如abcdefg,要求numRows = 3
a e
b d f
c
好,我们可以这样,准备一个矩阵,根据坐标(x,y)排列,就拿刚刚那个示例来说
我们先完成第一列,即先固定y,然后x一直到numRows-1位置停止
再斜向上走,一直到x = 0时再固定y,向下走,重复过程
class Solution {
public String convert(String s, int numRows) {
if (numRows == 1) return s;
//创建每行的 StringBuilder
StringBuilder[] rows = new StringBuilder[numRows];
for (int i = 0; i < numRows; i++) {
rows[i] = new StringBuilder();
}
int currentRow = 0;
boolean goingDown = false; //初始方向
for (char c : s.toCharArray()) {
rows[currentRow].append(c);
//到达顶部或底部时改变方向
if (currentRow == 0 || currentRow == numRows - 1) {
goingDown = !goingDown;
}
//根据方向移动行
currentRow += goingDown ? 1 : -1;
}
//合并所有行
StringBuilder result = new StringBuilder();
for (StringBuilder row : rows) {
result.append(row);
}
return result.toString();
}
}
但是这样不免时间复杂度太高,我们试试找找规律
我们可以试试在举证中填入下标

class Solution {
public String convert(String s, int numRows) {
if(numRows == 1){
return s;
}
StringBuilder ret = new StringBuilder();
int distance = 2*numRows-2;
int length = s.length();
//处理第一行
for(int i = 0;i<length;i+=distance){
ret.append(s.charAt(i));
}
//处理中间行
for(int r = 1;r<numRows-1;r++){
for(int i = r,j = distance-r;i < length || j < length;i += distance,j += distance){
if(i < length){
ret = ret.append(s.charAt(i));
}
if(j < length){
ret = ret.append(s.charAt(j));
}
}
}
//处理最后一行
for(int i = numRows-1;i<length;i +=distance){
ret = ret.append(s.charAt(i));
}
return ret.toString();
}
}
四、外观数列
题目链接
这一题就是对每一行的描述,注意:描述每一行时候,描述的是相邻的同类型的元素
1 <–第一行
1 1 <–第二行,描述:上一行有1个1
2 1 <–第三行,描述:上一行有2个1
1 2 1 1 <–第四行,描述:上一行有1个2,1个1
1 1 1 2 2 1 <–第五行,描述:上一行有1个1,1个2,两个1
3 1 2 2 1 1 <–第六行,描述:上一行有3个1,2个2,1个1
…
我们可以接住双指针模拟,同时在起始位,向后寻找与前一个指针不同的位置,然后统计个数
之后前一个指针移动到现在指针的位置,继续向后走…
class Solution {
public String countAndSay(int n) {
String str = "1";
//第一行不用描述了
for(int i = 1;i<n;i++){
StringBuilder ret = new StringBuilder();
int left = 0;
int right = 0;
int length = str.length();
while(right < length){
while(right < length && str.charAt(right) == str.charAt(left)){
right++;
}
//计算个数
ret.append(right-left);
//该个数属于哪个元素类型
ret.append(str.charAt(left));
left = right;
}
str = ret.toString();
}
return str;
}
}
五、数青蛙
题目链接
这道题重点在于理解

因此我们要用一个哈希表统计出现次数,具体请看

总结一下
- 碰到
c字符,找到最后字符k,然后判断其内部的值- 如果值是0,说明还没有任何一只青蛙叫完,直接当前字符
c++就好 - 如果不是0,说明已经有青蛙叫完,我们的
k值要 - -,当前字符c++
- 如果值是0,说明还没有任何一只青蛙叫完,直接当前字符
- 如果是其他字符,找到其前驱字符的值
- 如果前驱字符的值不为0,那就前驱字符的个数 - -,当前字符个数++
- 否则直接返回-1,不再需要往后遍历
- 如果是合法的字符串,最后在哈希表中只会剩下字符
k有值,其他值都为0,因为青蛙都叫完了嘛
class Solution {
public int minNumberOfFrogs(String croakOfFrogs) {
char[] croakOfFrog = croakOfFrogs.toCharArray();
String t = "croak";
int n = t.length();
//数组模拟哈希表,hash[0]对应字符c,值对应出现个数
int[] hash = new int[n];
//我们每次要找到当前字符的前驱字符,因此需要哈希表去给字符命名其下标
//即字符和下标的哈希映射关系
//[x, x这个字符对应的下标],找前驱节点时仅需要下标减一就好
Map<Character, Integer> index = new HashMap<>();
for(int i = 0; i < n; i++){
index.put(t.charAt(i), i);
}
for(char ch : croakOfFrog){
//字符等于c的情况
if(ch == t.charAt(0)){
//最后一个字符k判断其内部的值
if(hash[n - 1] != 0){
hash[n - 1]--;
}
hash[0]++;
}else{
//寻找前驱字符
int i = index.get(ch);
if(hash[i - 1] == 0){
return -1;
}
hash[i - 1]--; hash[i]++;
}
}
//判断在哈希表中是否存在除字符k以外的字符值不为0的情况
for(int i = 0; i < n - 1; i++){
if(hash[i] != 0){
return -1;
}
}
//返回字符k的值就好
return hash[n - 1];
}
}
731

被折叠的 条评论
为什么被折叠?



