2023A卷,完美走位

👨‍⚕️ 主页: gis分享者

👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!

👨‍⚕️ 收录于专栏:华为OD面试

文章目录

  • 一、🍀前言
    • [1.1 ☘️题目详情](#1.1 ☘️题目详情)
    • [1.2 ☘️参考解题答案](#1.2 ☘️参考解题答案)

一、🍀前言

2023A卷,完美走位。

1.1 ☘️题目详情

题目:

在第一人称射击游戏中,玩家通过键盘的 A、S、D、W 四个按键控制游戏人物分别向左、向后、向右、向前进行移动,从而完成走位。

假设玩家每按动一次键盘,游戏人物会向某个方向移动一步,如果玩家在操作一定次数的键盘并且各个方向的步数相同时,此时游戏人物必定会回到原点,则称此次走位为完美走位。

现给定玩家的走位(例如:ASDA),请通过更换其中一段连续走位的方式使得原走位能够变成一个完美走位。其中待更换的连续走位可以是相同长度的任何走位。

请返回待更换的连续走位的最小可能长度。若果原走位本身是一个完美走位,则返回 0。

输入:

输入为由键盘字母表示的走位s,例如:ASDA。

输出:

输出为待更换的连续走位的最小可能长度。

备注:

走位长度 1 ≤ s.length ≤ 10^5

s.length 是 4 的倍数

s 中只含有 A, S, D, W 四种字符

示例一:

javascript 复制代码
// 输入
ASDW
// 输出
0
// 说明 已经是完美走位了。

示例二:

javascript 复制代码
// 输入
AASW
// 输出
1
// 说明 需要把一个 A 更换成 D,这样可以得到 ADSW 或者 DASW。

示例三:

javascript 复制代码
// 输入
AAAA
// 输出
3
// 说明 可以替换后 3 个 A,得到 ASDW。

1.2 ☘️参考解题答案

解题思路

题目有两个重要条件:

  • 1.完美走位字符串是指字符串中A、5、D、w四种字符出现次数相等的字符串
  • 2.s.length 是4的倍数

对于长度为 1en(s)的原字符串;来说,为了使其转变为一个完美走位字符串,其中A、5、D、w四种字符出现次数应该均为num=len(s)//4。

原字符串:中各个字符出现的次数可以用哈希表 cnts=counter(s)进行统计,对于出现次数多于num=len(s)//4的字符ch,应该修改 cnt s[ch]-1en(s)//4 个字符为其他出现次数少于num=len(s)//4的字符,才能够使得s变为一个完美走位字符串。

以示例四为例,5="AAAAADDD",字符"A"出现的次数为5,字符""应该修改3,而num=len(s)//4=2,需要修改 3个""和1个""为剩余两种字符,才能使得,变为完美走位字符串。故我们需要找到包含3个""和1个""的最小子串。

因此这个问题就转变为了,找到覆盖 cnt_s[ch]-len(s)//4个的字符 ch(ch满足条件 cnt_s[ch]>len(s)//4)的最短子串。需要覆盖的子串中所出现的字符以及次数,可以用另一个哈希表cnt sub 储存。那么这个问题直接滑窗三问三答解决即可。上述逻辑整理为代码即

javascript 复制代码
num = len(s) // 4
cnt_s = Counter(s)
cnt_sub = {k: v-num for k, v in cnt_s.items() if v > num}

Python实现:

python 复制代码
# 题目:2023Q1A-完美走位
# 分值:200
# 作者:许老师-闭着眼睛学数理化
# 算法:不定滑窗
# 代码看不懂的地方,请直接在群上提问


from collections import Counter
from math import inf


# 定义辅助函数check()
# 用于检查cnt_sub中的各个字符是否出现在cnt_win中,
# 且cnt_win中的个数大于等于cnt_sub
def check(cnt_win, cnt_sub):
    return all(cnt_win[k] >= cnt_sub[k] for k in cnt_sub)


s = input()
num = len(s) // 4
# 获得原字符串中所有字符的出现次数
cnt_s = Counter(s)
# 获得需要覆盖的子串的字符以及出现次数
cnt_sub = {k: v-num for k, v in cnt_s.items() if v > num}

# 如果cnt_sub长度为0,说明每一种字符出现次数相等
# s已经是一个完美走位字符串,输出0
if len(cnt_sub) == 0:
    print(0)
# 否则要进行类似LC76. 最小覆盖子串的不定滑窗过程
else:
    # 初始化滑窗对应的哈希表、最小覆盖的窗口长度
    cnt_win = Counter()
    ans = inf
    left = 0

    for right, ch in enumerate(s):
        # Q1:对于每一个右指针right所指的元素ch,做什么操作?
        # A1:将其加入哈希表cnt_win的计数中
        cnt_win[ch] += 1
        # Q2:什么时候要令左指针left右移?在什么条件下left停止右移?【循环不变量】
        # A2:check(cnt_win, cnt_sub)为True,left可以右移以缩小窗口长度
        while check(cnt_win, cnt_sub):
            # Q3:什么时候进行ans的更新?
            # A3:check(cnt_win, cnt_sub)为True
            ans = min(ans, right-left+1)
            cnt_win[s[left]] -= 1
            left += 1

    print(ans)

c++实现:

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <unordered_map>
#include <climits>
using namespace std;

bool check(const unordered_map<char, int> &cntWin, const unordered_map<char, int> &cntSub) {
    for (const auto &entry : cntSub) {
        if (cntWin.find(entry.first) == cntWin.end() || cntWin.at(entry.first) < entry.second) {
            return false;
        }
    }
    return true;
}

int main() {
    string s;
    getline(cin, s);
    int num = s.length() / 4;

    unordered_map<char, int> cntS;
    for (char ch : s) {
        cntS[ch]++;
    }

    unordered_map<char, int> cntSub;
    for (const auto &entry : cntS) {
        if (entry.second > num) {
            cntSub[entry.first] = entry.second - num;
        }
    }

    if (cntSub.empty()) {
        cout << 0 << endl;
    } else {
        unordered_map<char, int> cntWin;
        int ans = INT_MAX;
        int left = 0;

        for (int right = 0; right < s.length(); right++) {
            char ch = s[right];
            cntWin[ch]++;

            while (check(cntWin, cntSub)) {
                ans = min(ans, right - left + 1);
                char leftChar = s[left];
                cntWin[leftChar]--;
                left++;
            }
        }

        cout << ans << endl;
    }

    return 0;
}

java实现:

java 复制代码
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String s = scanner.nextLine();
        int num = s.length() / 4;

        Map<Character, Integer> cntS = new HashMap<>();
        for (char ch : s.toCharArray()) {
            cntS.put(ch, cntS.getOrDefault(ch, 0) + 1);
        }

        Map<Character, Integer> cntSub = new HashMap<>();
        for (Map.Entry<Character, Integer> entry : cntS.entrySet()) {
            if (entry.getValue() > num) {
                cntSub.put(entry.getKey(), entry.getValue() - num);
            }
        }

        if (cntSub.isEmpty()) {
            System.out.println(0);
        } else {
            Map<Character, Integer> cntWin = new HashMap<>();
            int ans = Integer.MAX_VALUE;
            int left = 0;

            for (int right = 0; right < s.length(); right++) {
                char ch = s.charAt(right);
                cntWin.put(ch, cntWin.getOrDefault(ch, 0) + 1);

                while (check(cntWin, cntSub)) {
                    ans = Math.min(ans, right - left + 1);
                    char leftChar = s.charAt(left);
                    cntWin.put(leftChar, cntWin.get(leftChar) - 1);
                    left++;
                }
            }

            System.out.println(ans);
        }
    }

    public static boolean check(Map<Character, Integer> cntWin, Map<Character, Integer> cntSub) {
        for (Map.Entry<Character, Integer> entry : cntSub.entrySet()) {
            if (cntWin.getOrDefault(entry.getKey(), 0) < entry.getValue()) {
                return false;
            }
        }
        return true;
    }
}

时空复杂度

时间复杂度: 0(N)。仅需一次遍历整个字符串;。

空间复杂度: 0(1)。只有4种字符,哈希表所占用空间为常数级别空间。

相关推荐
零雲3 小时前
java面试:怎么保证消息队列当中的消息丢失、重复问题?
java·开发语言·面试
程序员祥云3 小时前
华为松山湖技术面
面试
踏浪无痕3 小时前
高并发写入 API 设计:借鉴 NSQ 的内存队列与背压机制
后端·面试·go
uhakadotcom3 小时前
Tomli 全面教程:常用 API 串联与实战指南
前端·面试·github
better_liang3 小时前
每日Java面试场景题知识点之-单例模式
java·单例模式·设计模式·面试·企业级开发
Java天梯之路4 小时前
手撸 Spring 简易版 AOP
java·spring boot·面试
Java天梯之路4 小时前
Spring AOP 源码深度解析:从代理创建到通知执行的完整链路
java·spring boot·面试
努力学算法的蒟蒻4 小时前
day31(12.11)——leetcode面试经典150
面试·职场和发展
阿拉伯柠檬4 小时前
C++中的继承
开发语言·数据结构·c++·面试