虾皮笔试 虾皮笔试题 0803
笔试时间:2025年8月3日
往年笔试合集:
第一题:最小覆盖子串
给定字符串 src和target, 找出src中最短的(连续)子串 sub,使得 targe是sub 的子序列。如果 src 中没有窗口可以包含 target 中的所有字符,返回空字符串""。如果有不止一个最短长度的窗口,返回开始位置最靠左的那个。
提示:s1是s2子序列的含义是,字符串s2与s1完全相同,或字符串s2去掉部分字符后可以与s1完全相同。注:1.所有输入的字符串都只能包含小写字母。
src 长度的范围为[1,20000]。3.target 长度的范围为|1,100]。其他: 时间限制:3000ms 内存限制:256.0MB
样例输入
s = "ADOBECODEBANC", t = "ABC"
样例输出
"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
参考题解
我们先使用一个哈希表(如 Counter)来统计字符串 t 中每个字符需要出现多少次。这个表我们称为 cnt_t。
它就像是我们的“目标清单”,表示:一个有效子串必须至少包含这些字符和数量。
我们定义两个指针 l 和 r 来表示当前窗口的左右边界(l 是左边界,r 是右边界),窗口的内容就是 s[l:r+1]。
同时,我们使用另一个哈希表 cnt_w 来记录当前窗口中每个字符的数量,方便判断当前窗口是否满足 cnt_t 的要求。
我们从左到右遍历字符串 s,每次将 s[r] 加入窗口并更新 cnt_w。
这样做是为了逐步构建一个可能满足要求的窗口。
每当我们发现当前窗口 cnt_w 已经满足了 cnt_t 的所有要求,就尝试不断移动左指针 l,看看能不能进一步缩小窗口,从而找到更短的答案。
每次成功缩小窗口并且满足条件时,就更新当前最小子串的结果。
为了判断一个窗口是否满足要求,我们写了一个 check() 函数:
- 遍历 cnt_t 中的所有字符;
- 如果当前窗口中某个字符的数量少于要求,就返回 False;
- 否则说明这个窗口是有效的。
在遍历完字符串之后,返回我们记录下的最小有效子串 res,即为最终答案。
C++:
#include <iostream>
#include <unordered_map>
#include <string>
#include <climits>
using namespace std;
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int> cnt_t, cnt_w;
// 初始化目标字符计数
for (char c : t) {
cnt_t[c]++;
}
int left = 0;
string result = "";
int min_len = INT_MAX;
int required = cnt_t.size();
int formed = 0;
for (int right = 0; right < s.size(); right++) {
char c = s[right];
cnt_w[c]++;
// 如果当前字符是目标字符且计数已满足,增加formed
if (cnt_t.find(c) != cnt_t.end() && cnt_w[c] == cnt_t[c]) {
formed++;
}
// 当窗口包含所有目标字符时,尝试收缩左边界
while (left <= right && formed == required) {
char left_char = s[left];
// 更新最小窗口
int current_len = right - left + 1;
if (current_len < min_len) {
min_len = current_len;
result = s.substr(left, current_len);
}
// 移动左边界,更新计数
if (cnt_t.find(left_char) != cnt_t.end()) {
if (cnt_w[left_char] == cnt_t[left_char]) {
formed--;
}
cnt_w[left_char]--;
}
left++;
}
}
return result;
}
};
int main() {
Solution solution;
string s = "ADOBECODEBANC";
string t = "ABC";
cout << solution.minWindow(s, t) << endl; // 输出 "BANC"
return 0;
}
Java:
import java.util.HashMap;
import java.util.Map;
public class Solution {
public String minWindow(String s, String t) {
Map<Character, Integer> cntT = new HashMap<>();
Map<Character, Integer> cntW = new HashMap<>();
// 初始化目标字符计数
for (char c : t.toCharArray()) {
cntT.put(c, cntT.getOrDefault(c, 0) + 1);
}
int left = 0;
String result = "";
int minLen = Integer.MAX_VALUE;
int required = cntT.size();
int formed = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
cntW.put(c, cntW.getOrDefault(c, 0) + 1);
// 如果当前字符是目标字符且计数已满足,增加formed
if (cntT.containsKey(c) && cntW.get(c).intValue() == cntT.get(c).intValue()) {
formed++;
}
// 当窗口包含所有目标字符时,尝试收缩左边界
while (left <= right && formed == required) {
char leftChar = s.charAt(left);
// 更新最小窗口
int currentLen = right - left + 1;
if (currentLen < minLen) {
minLen = currentLen;
result = s.substring(left, right + 1);
}
// 移动左边界,更新计数
if (cntT.containsKey(leftChar)) {
if (cntW.get(leftChar).intValue() == cntT.get(leftChar).intVal
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南

