前言
这是刷算法题的第三天,比较生疏,用到的语言是JS 题目:力扣 3216. 交换后字典序最小的字符串(简单)
一、题目内容
给你一个仅由数字组成的字符串 s,在最多交换一次 相邻 且具有相同 奇偶性 的数字后,返回可以得到的字典序最小的字符串。
如果两个数字都是奇数或都是偶数,则它们具有相同的奇偶性。例如,5 和 9、2 和 4 奇偶性相同,而 6 和 9 奇偶性不同。
示例 1:
输入: s = "45320"
输出: "43520"
解释:
s[1] == '5' 和 s[2] == '3' 都具有相同的奇偶性,交换它们可以得到字典序最小的字符串。
示例 2:
输入: s = "001"
输出: "001"
解释:
无需进行交换,因为 s 已经是字典序最小的。
提示:
2 <= s.length <= 100
s 仅由数字组成。
二、解题方法
1.暴力解法
解题思路
遍历字符串: 从字符串的左侧开始遍历,检查每一对相邻的数字,判断它们的奇偶性是否相同。如果相同,则可以进行交换。
生成新的字符串: 在找到可以交换的一对数字后,生成一个新的字符串,进行交换,并比较该字符串与当前记录的字典序最小字符串。如果新生成的字符串字典序更小,则更新字典序最小字符串的记录。
返回结果: 最后返回字典序最小的字符串。
代码如下(示例):
/**
* @param {string} s
* @return {string}
*/
var getSmallestString = function(s) {
// 实现思路:遍历循环:如果奇偶性相同,则进行这两个相邻字符的交换,且前面和后面的不变,并保存为新的字符串;
// 保存后与旧的字符串进行比较,如果新字符串字典序更小,则替换掉旧字符串
let string = s
let newstring = ''
// 此处必须 "-1" ,因为倒数第二个自然会跟最后一个比较
for( let i = 0; i < s.length - 1; i++ ) {
// 如果奇偶性相同
if(s[i] % 2 === s[i+1] % 2) {
newstring = s.slice(0, i) + s[i + 1] + s[i] + s.slice(i + 2)
// 此处是交换后才会进行比较,若提前交换会出问题,详见下面 3.
if(newstring < string) {
string = newstring
break
}
}
}
return string
};
2.官方题解
方法一:枚举
思路与算法:
题目要求我们最多交换一次相邻字符,并且满足交换的两个字符所表示的数字奇偶性相同,使得最终得到的字符串字典序最小。
我们知道,若两个字符串长度相同,则拥有首个不同字符中较小那个的字符串字典序更小,因此我们在枚举相邻两个字符交换时需要尽早的交换,并且交换之前后面的字符要小于前面的字符。
代码如下(示例):
var getSmallestString = function(s) {
let arr = s.split('');
for (let i = 0; i + 1 < arr.length; i++) {
if (arr[i] > arr[i + 1] && arr[i].charCodeAt(0) % 2 === arr[i + 1].charCodeAt(0) % 2) {
[arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
break;
}
}
return arr.join('');
};
作者:力扣官方题解
链接:https://leetcode.cn/problems/lexicographically-smallest-string-after-a-swap/solutions/2964922/jiao-huan-hou-zi-dian-xu-zui-xiao-de-zi-gngnj/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析:
时间复杂度:O(n),其中 n 是 s 的长度。整个过程我们只遍历一次字符串,并且最多只交换一次相邻字符。
空间复杂度:O(1)。过程中只使用了若干个变量。对于字符串不可修改的类型,需要先转换成列表进行处理,此时空间复杂度为 O(n)。
链接: 力扣本题官方题解
来源: 力扣(LeetCode)
3.错误解法
3.1 错误解法1
/**
* @param {string} s
* @return {string}
*/
var getSmallestString = function(s) {
// 实现思路:遍历循环:如果奇偶性相同,则进行这两个相邻字符的交换,且前面和后面的不变,并保存为新的字符串;
// 保存后与旧的字符串进行比较,如果新字符串字典序更小,则替换掉旧字符串
let string = s
let newstring = ''
// 此处必须 "-1" ,因为倒数第二个自然会跟最后一个比较
for( let i = 0; i < s.length - 1; i++ ) {
// 如果奇偶性相同
if(s[i] % 2 === s[i+1] % 2) newstring = s.slice(0, i) + s[i + 1] + s[i] + s.slice(i + 2) // 出错的地方
if(newstring < string) string = newstring // 出错的地方
}
return string
};
出现的问题:在循环内,将 newstring 赋值为一个新的字符串。但初始化 newstring 为一个空字符串,这样在第一轮循环时,newstring 可能与 string 发生比较,但如果没有进行交换,就会一直比较空字符串,这可能导致错误的结果。
3.2 错误解法2
/**
* @param {string} s
* @return {string}
*/
var getSmallestString = function(s) {
// 实现思路:遍历循环:如果奇偶性相同,则进行这两个相邻字符的交换,且前面和后面的不变,并保存为新的字符串;
// 保存后与旧的字符串进行比较,如果新字符串字典序更小,则替换掉旧字符串
let string = s
let newstring = ''
// 此处必须 "-1" ,因为倒数第二个自然会跟最后一个比较
for( let i = 0; i < s.length - 1; i++ ) {
// 如果奇偶性相同
if(s[i] % 2 === s[i+1] % 2) {
newstring = s.slice(0, i) + s[i + 1] + s[i] + s.slice(i + 2)
// 此处是交换后才会进行比较
if(newstring < string) string = newstring
break // 出错的地方
}
}
return string
};
出错的问题:break位置不对——无论比较与否,都只会进行一次比较,然后直接结束掉循环 举例:
输入:131 输出:131 正确:113
4.补充——字典序最小的字符串
字典序(或字典排序)是指按照某种顺序排列字符串或序列的一种规则,通常是依据字符的ASCII或Unicode值来进行比较。字典序的比较方式类似于我们在字典中查找单词的方式。
字典序的规则
字符串按照字符从左到右进行比较,如果第一不同的字符在一个字符串中比另一个字符串小,那么这个字符串就被认为是字典序更小的字符串。例如,"apple" < "banana"。 如果两个字符串的前缀相同,且一个字符串是另一个字符串的前缀,那么较短的字符串被认为是较小的字符串。例如,"app" < "apple"。 数字和字母的比较:如果字符串包含数字和字母,比较时通常会先比较字符的ASCII值,例如字符 '0' 的ASCII值小于字符 'A'。 字典序最小的字符串: 在某一组字符串中,字典序最小的字符串是指在所有可能的字符串中,按照字典序排列时排在最前面的那个字符串。例如,在字符串集合 {"apple", "banana", "apricot"} 中,"apple" 是字典序最小的字符串。
在程序设计中,通常可以使用字符串比较运算符来直接判断一个字符串是否字典序更小或更大的操作。
例子
对于字符串 "car"、"bat"、"apple",按字典序排列为: "apple" < "bat" < "car" 所以 "apple" 是字典序最小的字符串。 在本问题中,要求在最多交换一次相邻的、具有相同奇偶性的数字后,得到的字符串中哪个是字典序最小的字符串,这意味着找到所有可能的字符串,再从中选择最小的一个。