持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
[面试题 17.05. 字母与数字](leetcode.cn/problems/lr…)
给定一个放有字母和数字的数组,找到最长的子数组,且包含的字母和数字的个数相同。
返回该子数组,若存在多个最长子数组,返回左端点下标值最小的子数组。若不存在这样的数组,返回一个空数组。
「示例1:」
输入: ["A","1","B","C","D","2","3","4","E","5","F","G","6","7","H","I","J","K","L","M"]
输出: ["A","1","B","C","D","2","3","4","E","5","F","G","6","7"]
「示例2:」
输入: ["A","A"]
输出: []
解题思路
前缀:数组中的前缀是指从左到右,长度小于或者等于数组长度的子数组;
前缀和:数组的前缀子数组的和;
大体思路如下:
由于数组中只存在字母和数字两种情况,因此我们可以将字母记为-1,数字记为1来简化问题;
题目要求子数组中的字符和数字个数相同,因此我们可以考虑通过求解前缀和来解决这个问题,解分为两种情况:
两个前缀和的值相同,例如:prefix[i]==prefix[j],说明[0,i]和[0,j]之间多余数字或字符的个数相同,那么如果我们用区间[0,j]减去区间[0,i]之后,在(i,j]之间的数字和字符个数必然相同,因此[(i,j]可能是一组解(注意此处得区间是左开右闭的);
前缀和为0处,例如:prefix[i]=0,说明 [0,i]之间的数字和字符个数相同,所以[0,j]可能是一组解;
由于题目中需要求得最长的子数组,并且长度相同时以左边最小的子数组作为开头,因此最终答案需要找到前缀和相同的子数组和前缀和为0的子数组最大的那一个即可。
因此,我们先将原数组转换成-1和1的形式,并求出array的所有前缀和;
然后,我们遍历一次前缀和数组,并用一个映射(字典)inordered_map<int,int> 来记录前缀和的值和它的最左边的下标位置;
每一次当我们找到在前面出现过的前缀和或是前缀和为0的情况,我们就按照规则来更新答案。
如上图所示,题目中给的例子的答案是第一个前缀和为1的位置到最后一个前缀和为1的位置的子数组。
代码实现
var findLongestSubarray = function (array) {
let sum = 0
const map = new Map()
let front = 0, last = 0
map.set(0, -1)
array.forEach((item, index) => {
sum += (array[index][0] >= 'A' && array[index][0] <= 'z' ? 1 : -1)
if (map.has(sum)) {
if (index - map.get(sum) > last - front) {
last = index
front = map.get(sum)
}
} else {
map.set(sum, index)
}
})
return array.slice(front + 1, last + 1)
};
如果你对这道题目还有疑问的话,可以在评论区进行留言;