这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战
题目
1487. 保证文件名唯一
给你一个长度为 n
的字符串数组 names
。你将会在文件系统中创建 n
个文件夹:在第 i
分钟,新建名为 names[i]
的文件夹。
由于两个文件 不能 共享相同的文件名,因此如果新建文件夹使用的文件名已经被占用,系统会以 (k)
的形式为新文件夹的文件名添加后缀,其中 k
是能保证文件名唯一的 最小正整数 。
返回长度为 n
的字符串数组,其中 ans[i]
是创建第 i
个文件夹时系统分配给该文件夹的实际名称。
示例 1:
输入: names = ["pes","fifa","gta","pes(2019)"]
输出: ["pes","fifa","gta","pes(2019)"]
解释: 文件系统将会这样创建文件名:
"pes" --> 之前未分配,仍为 "pes"
"fifa" --> 之前未分配,仍为 "fifa"
"gta" --> 之前未分配,仍为 "gta"
"pes(2019)" --> 之前未分配,仍为 "pes(2019)"
示例 2:
输入: names = ["gta","gta(1)","gta","avalon"]
输出: ["gta","gta(1)","gta(2)","avalon"]
解释: 文件系统将会这样创建文件名:
"gta" --> 之前未分配,仍为 "gta"
"gta(1)" --> 之前未分配,仍为 "gta(1)"
"gta" --> 文件名被占用,系统为该名称添加后缀 (k),由于 "gta(1)" 也被占用,所以 k = 2 。实际创建的文件名为 "gta(2)" 。
"avalon" --> 之前未分配,仍为 "avalon"
示例 3:
输入: names = ["onepiece","onepiece(1)","onepiece(2)","onepiece(3)","onepiece"]
输出: ["onepiece","onepiece(1)","onepiece(2)","onepiece(3)","onepiece(4)"]
解释: 当创建最后一个文件夹时,最小的正有效 k 为 4 ,文件名变为 "onepiece(4)"。
示例 4:
输入: names = ["wano","wano","wano","wano"]
输出: ["wano","wano(1)","wano(2)","wano(3)"]
解释: 每次创建文件夹 "wano" 时,只需增加后缀中 k 的值即可。
示例 5:
输入: names = ["kaido","kaido(1)","kaido","kaido(1)"]
输出: ["kaido","kaido(1)","kaido(2)","kaido(1)(1)"]
解释: 注意,如果含后缀文件名被占用,那么系统也会按规则在名称后添加新的后缀 (k) 。
提示:
1 <= names.length <= 5 * 10^4
1 <= names[i].length <= 20
names[i]
由小写英文字母、数字和/或圆括号组成。
思路
-
这道题目比较容易陷入一个误区,就是我得拆解后面的几个括号,然后提取里面的数字进行一个累加。实际上我们并不需要关心括号里面的数字是多少;
-
我们只需要用一个
set
对象来记录当前的文件名之前有没有出现过,如果出现过的话,我们就把它丢进文件名累加的方法里面去,如果没有出现过的话,我们就记录它的存在即可; -
然后再说一下文件名累加的方法,我们判断当前的文件名后面加
(count)
是否存在,如果存在的话count++
,不存在则直接修改即可。这个count
的值一开始是从1
开始,然后我们遍历去判断它是否已经存在即可。 -
这样子我们就可以通过一轮循环实现我们的结果,当然在
count
累加的时候,最坏情况是每次都计算一遍数量,所以复杂度上最坏的话是遍历两次嵌套数组O(n²)。
实现
/**
* @param {string[]} names
* @return {string[]}
*/
var getFolderNames = function(names) {
// 记录当前的对象是否出现过
let set = new Set();
// 处理添加函数后缀的方法, 遇到需要加的放进来
function handleAddNumber(index, count = 1) {
// 当前的索引已经有值了,那么就递增
while (set.has(`${names[index]}(${count})`)) {
count++;
}
names[index] = `${names[index]}(${count})`;
set.add(names[index]);
}
for (let i = 0; i < names.length; i++) {
// 判断这个元素是否出现过,出现过的话丢到递增里面去
if (set.has(names[i])) {
handleAddNumber(i);
} else {
// 没出现的话记录下来即可
set.add(names[i]);
}
}
return names;
};
优化
-
上面说到了我们每次找元素添加后缀的时候,每次都得从头遍历一次,那么我们其实可以想着把
set
方法改成map
方法,然后每个元素我们去记录他到了第几位了,这样子我们就可以省去一个查找的过程; -
但是是完全不查找吗?其实也不是,因为我们前面记录完后,原本的位置突然直接插进来一个元素,我们是不知道的,除非我们切掉最后一个括号去给它的前缀做累加,但是显然这样子代码看起来就更麻烦了;
-
所以的话我们还是会检查,只是检查之前每一次都会提前记录一遍,帮我们省下重复操作的那一部分时间。
最终代码
/**
* @param {string[]} names
* @return {string[]}
*/
var getFolderNames = function(names) {
// 记录当前的对象的最后一个索引
let map = new Map();
for (let i = 0; i < names.length; i++) {
// 判断这个元素是否出现过,出现过的话丢到递增里面去
if (map.has(names[i])) {
let count = map.get(names[i]) + 1;
while (map.has(names[i] + `(${count})`)) {
count++;
}
map.set(names[i], count);
names[i] += `(${count})`;
}
// 记录下当前的元素
map.set(names[i], 0);
}
return names;
};
最终结果
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。