面试题 17.05. 字母与数字

面试题 17.05. 字母与数字

难度:

m

i

d

d

l

e

\color{orange}{middle}

middle


题目描述

给定一个放有字母和数字的数组,找到最长的子数组,且包含的字母和数字的个数相同。

返回该子数组,若存在多个最长子数组,返回左端点下标值最小的子数组。若不存在这样的数组,返回一个空数组。

示例 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"]

输出: []

提示:

  • a

    r

    r

    a

    y

    .

    l

    e

    n

    g

    t

    h

    <

    =

    100000

    array.length <= 100000

    array.length<=100000


算法

(前缀和 + 哈希表)

题目要求找到最长的子数组,且包含的字符和数字的个数相同。我们可以将字符看作 1,数字看作 −1,那么问题就转化为:求最长的子数组,使得该子数组的和为 0

我们可以运用前缀和的思想,用哈希表 hash 记录每个前缀和第一次出现的位置,用变量 mxk 分别记录最长的满足条件的子数组的长度和左端点位置。

接下来遍历数组,计算当前位置 i 的前缀和 s

  • 如果当前位置的前缀和 s 在哈希表 hash 中存在,我们记第一次出现 s 的位置为 j,那么区间 [j+1,..,i] 的子数组和就为 0。如果此前的最长子数组的长度小于当前子数组的长度,即 mx<i−j,我们就更新 mx=i−jk=j+1

  • 否则,我们将当前位置的前缀和 s 作为键,当前位置 i 作为值,存入哈希表hash 中。

遍历结束后,返回左端点为 k,且长度为 mx 的子数组即可。

复杂度分析

  • 时间复杂度

    O

    (

    n

    )

    O(n)

    O(n)

  • 空间复杂度 :

    O

    (

    n

    )

    O(n)

    O(n)

C++ 代码

class Solution {
public:
    vector<string> findLongestSubarray(vector<string>& array) {
    //这里是一个初始化操作,把第一次出现0的点初始化为-1,这样当j为0时i-j的长度是一个正确的值
        unordered_map<int, int> mp{{0, -1}};
        // s表示前缀和,mx表示最长子数组的长度,k表示最长子数组的左端点
        int s = 0, mx = 0, k = 0;
        for (int i = 0; i < array.size(); i++) {
            // 标定是否为字母,是则为1,不是则为-1
            s += array[i][0] >= 'A' ? 1 : -1; 
            if (mp.find(s) != mp.end()) {
                // 判断哈希表中是否存在相同的s
                // 如果存在,则接收它的左端点
                int j = mp[s];
                if (i - j > mx) {
                    // 判断数组长度i-j是否大于mx
                    // 如果是,则更新数组长度
                    mx = i - j;
                    // 同时更新左端点
                    // vector复制的范围是左闭右开,设左边界为j+1这样才能取到正确的子数组
                    k = j + 1;
                }
            } else {
                // 如果不存在相同的s,则将它加入哈希表中
                mp.emplace(s, i);
            }
        }
        // 最后,我们得到了最长子数组的左端点和长度,因此可以根据这两个值将最长子数组拷贝出来
        return vector<string>(array.begin() + k, array.begin() + k + mx);
    }
};