当前位置: 首页 > news >正文

【华为OD】完美走位

在这里插入图片描述

文章目录

  • 【华为OD】完美走位
    • 题目描述
      • 输入
      • 输出
      • 备注
    • 示例
      • 示例一
      • 示例二
      • 示例三
      • 示例四
    • 解题思路
    • 解法一:滑动窗口
      • 思路
      • Java实现
      • Python实现
      • C++实现
    • 解法二:前缀和 + 哈希表
      • 思路
      • Java实现
      • Python实现
      • C++实现
    • 复杂度分析
      • 解法一(滑动窗口)
      • 解法二(前缀和 + 哈希表)
    • 总结

【华为OD】完美走位

题目描述

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

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

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

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

输入

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

输出

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

备注

  1. 走位长度 1 ≤ s.length ≤ 10^5
  2. s.length 是 4 的倍数
  3. s 中只含有 A、S、D、W 四种字符

示例

示例一

输入:

ASDW

输出:

0

说明: 已经是完美走位了。

示例二

输入:

AASW

输出:

1

说明: 需要把一个 A 更换成 D,这样可以得到 ADSW 或者 DASW。

示例三

输入:

AAAA

输出:

3

说明: 可以替换后 3 个 A,得到 ASDW。

示例四

输入:

AAAAADDD

输出:

4

解题思路

本题的关键在于将原问题转化为覆盖子串的问题。

题目有两个重要条件:

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

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

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

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

因此这个问题就转变为了,找到覆盖 cnt_s[ch] - len(s) // 4 个字符 ch(ch 满足条件 cnt_s[ch] > len(s) // 4)的最短子串。

解法一:滑动窗口

思路

使用滑动窗口来找到包含所有需要替换字符的最短子串。

Java实现

import java.util.*;public class Solution {public int minReplaceLength(String s) {int n = s.length();if (n % 4 != 0) return -1;int target = n / 4;Map<Character, Integer> count = new HashMap<>();// 统计每个字符的出现次数for (char c : s.toCharArray()) {count.put(c, count.getOrDefault(c, 0) + 1);}// 找出需要减少的字符及其数量Map<Character, Integer> need = new HashMap<>();for (Map.Entry<Character, Integer> entry : count.entrySet()) {if (entry.getValue() > target) {need.put(entry.getKey(), entry.getValue() - target);}}// 如果已经是完美走位if (need.isEmpty()) return 0;// 滑动窗口int left = 0, right = 0;int minLen = n;Map<Character, Integer> window = new HashMap<>();while (right < n) {char c = s.charAt(right);window.put(c, window.getOrDefault(c, 0) + 1);right++;// 检查是否满足条件while (isValid(window, need)) {minLen = Math.min(minLen, right - left);char leftChar = s.charAt(left);window.put(leftChar, window.get(leftChar) - 1);if (window.get(leftChar) == 0) {window.remove(leftChar);}left++;}}return minLen;}private boolean isValid(Map<Character, Integer> window, Map<Character, Integer> need) {for (Map.Entry<Character, Integer> entry : need.entrySet()) {if (window.getOrDefault(entry.getKey(), 0) < entry.getValue()) {return false;}}return true;}
}

Python实现

from collections import Counter, defaultdictdef min_replace_length(s):n = len(s)if n % 4 != 0:return -1target = n // 4count = Counter(s)# 找出需要减少的字符及其数量need = {}for char, cnt in count.items():if cnt > target:need[char] = cnt - target# 如果已经是完美走位if not need:return 0# 滑动窗口left = 0min_len = nwindow = defaultdict(int)for right in range(n):window[s[right]] += 1# 检查是否满足条件while all(window[char] >= cnt for char, cnt in need.items()):min_len = min(min_len, right - left + 1)window[s[left]] -= 1if window[s[left]] == 0:del window[s[left]]left += 1return min_len

C++实现

#include <string>
#include <unordered_map>
#include <algorithm>
using namespace std;class Solution {
public:int minReplaceLength(string s) {int n = s.length();if (n % 4 != 0) return -1;int target = n / 4;unordered_map<char, int> count;// 统计每个字符的出现次数for (char c : s) {count[c]++;}// 找出需要减少的字符及其数量unordered_map<char, int> need;for (auto& p : count) {if (p.second > target) {need[p.first] = p.second - target;}}// 如果已经是完美走位if (need.empty()) return 0;// 滑动窗口int left = 0, right = 0;int minLen = n;unordered_map<char, int> window;while (right < n) {window[s[right]]++;right++;// 检查是否满足条件while (isValid(window, need)) {minLen = min(minLen, right - left);window[s[left]]--;if (window[s[left]] == 0) {window.erase(s[left]);}left++;}}return minLen;}private:bool isValid(const unordered_map<char, int>& window, const unordered_map<char, int>& need) {for (auto& p : need) {if (window.count(p.first) == 0 || window.at(p.first) < p.second) {return false;}}return true;}
};

解法二:前缀和 + 哈希表

思路

通过前缀和记录每个位置各字符的累计出现次数,然后枚举所有可能的子串长度,使用哈希表快速判断是否存在满足条件的子串。

Java实现

import java.util.*;public class Solution {public int minReplaceLength(String s) {int n = s.length();if (n % 4 != 0) return -1;int target = n / 4;Map<Character, Integer> count = new HashMap<>();// 统计每个字符的出现次数for (char c : s.toCharArray()) {count.put(c, count.getOrDefault(c, 0) + 1);}// 找出需要减少的字符及其数量Map<Character, Integer> need = new HashMap<>();for (Map.Entry<Character, Integer> entry : count.entrySet()) {if (entry.getValue() > target) {need.put(entry.getKey(), entry.getValue() - target);}}// 如果已经是完美走位if (need.isEmpty()) return 0;// 前缀和Map<Character, int[]> prefix = new HashMap<>();for (char c : "ASDW".toCharArray()) {prefix.put(c, new int[n + 1]);}for (int i = 0; i < n; i++) {char c = s.charAt(i);for (char ch : "ASDW".toCharArray()) {prefix.get(ch)[i + 1] = prefix.get(ch)[i] + (ch == c ? 1 : 0);}}// 枚举子串长度for (int len = 1; len <= n; len++) {for (int i = 0; i <= n - len; i++) {boolean valid = true;for (Map.Entry<Character, Integer> entry : need.entrySet()) {char c = entry.getKey();int needCount = entry.getValue();int actualCount = prefix.get(c)[i + len] - prefix.get(c)[i];if (actualCount < needCount) {valid = false;break;}}if (valid) return len;}}return n;}
}

Python实现

from collections import Counterdef min_replace_length(s):n = len(s)if n % 4 != 0:return -1target = n // 4count = Counter(s)# 找出需要减少的字符及其数量need = {}for char, cnt in count.items():if cnt > target:need[char] = cnt - target# 如果已经是完美走位if not need:return 0# 前缀和prefix = {c: [0] * (n + 1) for c in 'ASDW'}for i in range(n):c = s[i]for ch in 'ASDW':prefix[ch][i + 1] = prefix[ch][i] + (1 if ch == c else 0)# 枚举子串长度for length in range(1, n + 1):for i in range(n - length + 1):valid = Truefor char, need_count in need.items():actual_count = prefix[char][i + length] - prefix[char][i]if actual_count < need_count:valid = Falsebreakif valid:return lengthreturn n

C++实现

#include <string>
#include <unordered_map>
#include <vector>
using namespace std;class Solution {
public:int minReplaceLength(string s) {int n = s.length();if (n % 4 != 0) return -1;int target = n / 4;unordered_map<char, int> count;// 统计每个字符的出现次数for (char c : s) {count[c]++;}// 找出需要减少的字符及其数量unordered_map<char, int> need;for (auto& p : count) {if (p.second > target) {need[p.first] = p.second - target;}}// 如果已经是完美走位if (need.empty()) return 0;// 前缀和unordered_map<char, vector<int>> prefix;string chars = "ASDW";for (char c : chars) {prefix[c] = vector<int>(n + 1, 0);}for (int i = 0; i < n; i++) {char c = s[i];for (char ch : chars) {prefix[ch][i + 1] = prefix[ch][i] + (ch == c ? 1 : 0);}}// 枚举子串长度for (int len = 1; len <= n; len++) {for (int i = 0; i <= n - len; i++) {bool valid = true;for (auto& p : need) {char c = p.first;int needCount = p.second;int actualCount = prefix[c][i + len] - prefix[c][i];if (actualCount < needCount) {valid = false;break;}}if (valid) return len;}}return n;}
};

复杂度分析

解法一(滑动窗口)

  • 时间复杂度: O(n),其中 n 是字符串长度
  • 空间复杂度: O(1),只使用了常数个哈希表

解法二(前缀和 + 哈希表)

  • 时间复杂度: O(n²),需要枚举所有可能的子串
  • 空间复杂度: O(n),需要存储前缀和数组

总结

这道题的核心思想是将"完美走位"问题转化为"最短覆盖子串"问题。滑动窗口解法更加高效,时间复杂度为O(n);而前缀和解法思路更直观,但时间复杂度较高。在实际应用中,推荐使用滑动窗口解法。


文章转载自:

http://F0Xg6t6w.qbwyd.cn
http://2HOGbVQj.qbwyd.cn
http://J6xH5w4e.qbwyd.cn
http://BqZvvIuu.qbwyd.cn
http://NzQsGWHF.qbwyd.cn
http://nWw9bMeA.qbwyd.cn
http://cd1xAwvp.qbwyd.cn
http://w8EJXzni.qbwyd.cn
http://SggDo3DR.qbwyd.cn
http://HkgVwFpf.qbwyd.cn
http://CLshxkgn.qbwyd.cn
http://7Jylmzv4.qbwyd.cn
http://hKr12rQh.qbwyd.cn
http://QmQOlQnM.qbwyd.cn
http://o0pcSQoP.qbwyd.cn
http://L4cGR15R.qbwyd.cn
http://7aY2vZrA.qbwyd.cn
http://mCivTzYw.qbwyd.cn
http://aQxSTTQv.qbwyd.cn
http://Btbk7F7I.qbwyd.cn
http://Y84cGLYS.qbwyd.cn
http://0QwEIBYU.qbwyd.cn
http://Ru3hqFiz.qbwyd.cn
http://WngNIHPW.qbwyd.cn
http://fRj7Nrnv.qbwyd.cn
http://NUUsWDJK.qbwyd.cn
http://N4pOgdro.qbwyd.cn
http://u6NlWNhb.qbwyd.cn
http://BDwSijqu.qbwyd.cn
http://WmX84TS9.qbwyd.cn
http://www.dtcms.com/a/376534.html

相关文章:

  • Linux下运行芙蕾雅天堂2【俄文简译L2FATER】
  • 消息队列(MQ)高级特性深度剖析:详解RabbitMQ与Kafka
  • win11安装GnuWin32支持执行Makefile命令
  • 从原理到实践:LVS+Keepalived构建高可用负载均衡集群
  • 多脚本大批量训练
  • java与node.js对比
  • tailwindcss 究竟比 unocss 快多少?
  • 排序---希尔排序(Shell Sort)
  • Windows系统下,配置VScode的git以及git终端
  • 机器学习实战(一): 什么是机器学习
  • Google SEO 优化里,AWS 的隐藏优势
  • 微信推客小程序系统开发技术实践
  • git下载、安装、使用
  • Transformer实战(17)——微调Transformer语言模型进行多标签文本分类
  • 单例模式(C++)详解(3)
  • Eyeshot 2025.3 3D 图形工具包
  • 【Linux手册】信号量与建造者模式:以 PV 操作保证并发安全,分步组装构建复杂对象
  • 【展厅多媒体】VR虚拟现实,构建展厅沉浸体验的重要技术
  • 京东京造K2 蓝牙/有线双模键盘键盘快捷键
  • Figma Make 输入指令浏览器无响应
  • 【设计模式】【观察者模式】实例
  • 【Linux手册】消息队列从原理到模式:底层逻辑、接口实战与责任链模式的设计艺术
  • 学习React-10-useTransition
  • Hive中的3种虚拟列以及Hive如何进行条件判断
  • 基于 C++ 的 IEC60870-5-104 规约的主从站模拟数据通信
  • css flex布局,设置flex-wrap:wrap换行后,如何保证子节点被内容撑高后,每一行的子节点高度一致。
  • 一款免费开源轻量的漏洞情报系统 | 漏洞情报包含:组件漏洞 + 软件漏洞 + 系统漏洞
  • 容器问答题上
  • uniapp发布成 微信小程序 主包内 main.wxss 体积太大
  • Uniapp中使用renderjs实现OpenLayers+天地图的展示与操作