使用机器人打印字典序最小的字符串

108 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

🎈算法并不一定都是很难的题目,也有很多只是一些代码技巧,多进行一些算法题目的练习,可以帮助我们开阔解题思路,提升我们的逻辑思维能力,也可以将一些算法思维结合到业务代码的编写思考中。简而言之,平时进行的算法习题练习带给我们的好处一定是不少的,所以让我们一起来养成算法练习的习惯。今天练习的题目是一道中等难度的题目 -> 使用机器人打印字典序最小的字符串

题目描述

给你一个字符串 s 和一个机器人,机器人当前有一个空字符串 t 。执行以下操作之一,直到 s 和 t 都变成空字符串:

删除字符串 s 的 第一个 字符,并将该字符给机器人。机器人把这个字符添加到 t 的尾部。 删除字符串 t 的 最后一个 字符,并将该字符给机器人。机器人将该字符写到纸上。 请你返回纸上能写出的字典序最小的字符串。

示例 1:

输入:s = "zza"
输出:"azz"
解释:用 p 表示写出来的字符串。
一开始,p="" ,s="zza" ,t="" 。
执行第一个操作三次,得到 p="" ,s="" ,t="zza" 。
执行第二个操作三次,得到 p="azz" ,s="" ,t=""

示例 2:

输入:s = "bac"
输出:"abc"
解释:用 p 表示写出来的字符串。
执行第一个操作两次,得到 p="" ,s="c" ,t="ba" 。
执行第二个操作两次,得到 p="ab" ,s="c" ,t="" 。
执行第一个操作,得到 p="ab" ,s="" ,t="c" 。
执行第二个操作,得到 p="abc" ,s="" ,t=""

示例 3:

输入:s = "bdda"
输出:"addb"
解释:用 p 表示写出来的字符串。
一开始,p="" ,s="bdda" ,t="" 。
执行第一个操作四次,得到 p="" ,s="" ,t="bdda" 。
执行第二个操作四次,得到 p="addb" ,s="" ,t=""

提示:

  • 1 <= s.length <= 105
  • s 只包含小写英文字母。

思路分析

首先我们要先理解一下题目的意思,题目会给我们一个字符串,我们可以对该字符串进行以下操作:

  • 删除字符串 s 的 第一个 字符,并将该字符给机器人。机器人把这个字符添加到 t 的尾部。
  • 删除字符串 t 的 最后一个 字符,并将该字符给机器人。机器人将该字符写到纸上。

简单来说就是我们每一步都有两个选择

  • 1、将字符串s中的第一个字符拿出来堆入t这个栈中
  • 2、将t的最后一个字符出栈,拼接到纸上的字符串res后面

我们需要做的是让纸上的字符串res的字典序最小。

什么是字典序

在数学中,字典或词典顺序(也称为词汇顺序,字典顺序,字母顺序或词典顺序)是基于字母顺序排列的单词按字母顺序排列的方法。 这种泛化主要在于定义有序完全有序集合(通常称为字母表)的元素的序列(通常称为计算机科学中的单词)的总顺序。

设想一本英语字典里的单词,何者在前何者在后?

显然的做法是先按照第一个字母、以 a、b、c……z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,sigh 和 sight),那么把短者排在前。通过这种方法,我们可以给本来不相关的单词强行规定出一个顺序。“单词”可以看作是“字母”的字符串,而把这一点推而广之就可以认为是给对应位置元素所属集合分别相同的各个有序多元组规定顺序。

解题步骤

  • 1、统计字符串s中每个字母的个数 即当前字符串s中每一个字母的个数
let map = new Array(26).fill(0);
for(let i = 0; i < s.length; i++){
    map[s[i].charCodeAt() - 97]++;
}
  • 2、遍历字符串s,将字符串中的每一个字母入栈 对于字符串s中的字母取值,我们是从前往后的,所以我们只需要遍历一遍,将字母入栈即可,字母入栈之后需要将当前字母的统计数减一,并判断是否要将栈中的字母出栈
for(let i = 0; i < s.length; i++){
    stack.push(s[i]);
    map[s[i].charCodeAt() - 97]--;
    doPop();
}
  • 3、将栈中元素出栈写到纸上 要使得到的字典序越小,我们需要尽可能地将字母小的放在前面,我们使用map字典来统计了当前字符串s中每个字母的个数,我们只需要判断当前字符串s中是否还有比栈顶元素小的字母,如果字符串s中的最小字母比栈顶字母大的话,我们即可将栈顶的字母出栈记录到纸上,这样可以保证得到的字符串的字典序是最小的。
const doPop = ()=>{
    for(let i = 0; i < 26; i++){
        if(map[i] > 0){
            while(stack.length && stack[stack.length - 1].charCodeAt() - 97 <= i) res += stack.pop();
            break;
        }
    }
};

完整AC代码如下:

AC代码

/**
 * @param {string} s
 * @return {string}
 */
 var robotWithString = function(s) {
    let map = new Array(26).fill(0);
    let stack = [],res = '';
    for(let i = 0; i < s.length; i++){
        map[s[i].charCodeAt() - 97]++;
    }
    const doPop = ()=>{
        for(let i = 0; i < 26; i++){
            if(map[i] > 0){
                while(stack.length && stack[stack.length - 1].charCodeAt() - 97 <= i) res += stack.pop();
                break;
            }
        }
    };
    for(let i = 0; i < s.length; i++){
        stack.push(s[i]);
        map[s[i].charCodeAt() - 97]--;
        doPop();
    }
    res += stack.reverse().join('');
    return res;
};

说在后面

🎉这里是JYeontu,喜欢算法,GDCPC打过卡;热爱羽毛球,大运会打过酱油。毕业一年,两年前端开发经验,目前担任H5前端开发,算法业余爱好者,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。