华为OD-新A卷-获得完美走位 100分(python、java、c++、js、)
题目
在第一人称射击游戏中,玩家通过键盘的A、S、D、W四个按键控制游戏人物分别向左、向后、向右、向前进行移动,从而完成走位。假设玩家每按动一次键盘,游戏任务会向某个方向移动一步,如果玩家在操作一定次数的键盘并且各个方向的步数相同时,此时游戏任务必定会回到原点,则称此次走位为完美走位。现给定玩家的走位(例如:ASDA),请通过更换其中一段连续走位的方式使得原走位能够变成一个完美走位。其中待更换的连续走位可以是相同长度的任何走位。请返回待更换的连续走位的最小可能长度。如果原走位本身是一个完美走位,则返回0。
输入描述
输入为由键盘字母表示的走位s,例如:ASDA
输出描述
输出为待更换的连续走位的最小可能长度
备注
走位长度 1 ≤ s.length ≤ 100000 s.length 是 4 的倍数 s中只含有'A', 'S', 'D', 'W' 四种字符
用例
用例一:
输入:
WASDAASD
输出:
1
用例二:
输入:
AAAA
输出:
3
python解法
解题思路:
问题分析:给定一个只包含字符 "W", "A", "S", "D" 的字符串,要求找到一个最短的子串,使得移除该子串后,剩余字符串中 "W", "A", "S", "D" 的数量均相等。
方法:
首先统计字符串中每个字符的总数。计算每种字符的过剩数量,即超出目标的部分。如果没有过剩字符(所有字符数量都已经满足条件),直接返回 0。使用滑动窗口找到满足条件的最短子串:滑动窗口的右边界不断扩展,直到包含所有过剩字符。在满足条件的情况下,尝试通过收缩左边界,找到更短的子串。滑动窗口细节:
使用两个指针 start 和 end 分别表示窗口的左右边界。窗口内字符的数量变化由 excess_count 控制。窗口有效的条件是 excess_count 中所有值均不大于 0。
def min_length_substring(s):
"""
计算需要移除的最短子串长度,使得剩余字符串中WASD四种字符的数量相等
参数:
s (str): 输入的只包含WASD四种字符的字符串
返回:
int: 需要移除的最短子串长度
"""
# 统计字符串中各字符的数量
total_count = {"W": 0, "A": 0, "S": 0, "D": 0}
for char in s:
total_count[char] += 1
# 每种字符的目标数量(总长度除以4)
target = len(s) // 4
# 计算每种字符的过剩数量(超过目标值的部分)
excess_count = {char: max(0, count - target) for char, count in total_count.items()}
# 如果没有字符过剩(说明已经平衡),返回0
if sum(excess_count.values()) == 0:
return 0
# 初始化滑动窗口的指针和最短长度
start = 0
min_len = len(s) # 初始设为最大值
# 滑动窗口算法实现
for end in range(len(s)):
# 当前字符是过剩字符时,减少其过剩计数
if excess_count[s[end]] > 0:
excess_count[s[end]] -= 1
# 当所有过剩字符都被包含在窗口中时(即所有过剩计数<=0)
while all(count <= 0 for count in excess_count.values()):
# 更新最小长度
current_len = end - start + 1
if current_len < min_len:
min_len = current_len
# 尝试收缩左边界
# 如果左边界字符是过剩字符,恢复其计数
if s[start] in excess_count and total_count[s[start]] > target:
excess_count[s[start]] += 1
start += 1
return min_len
# 示例用法
if __name__ == "__main__":
# 输入字符串(只包含WASD四种字符)
s = input().strip()
# 检查输入是否有效
if not all(c in "WASD" for c in s):
print("输入字符串必须只包含W,A,S,D四种字符")
elif len(s) % 4 != 0:
print("字符串长度必须是4的倍数")
else:
# 计算并输出需要移除的最短子串长度
print(min_length_substring(s))
java解法
解题思路
问题描述:给定一个只包含字符 A、S、D、W 的字符串,找到一个最短子串,移除该子串后剩下的字符串中这四种字符的数量相等。
方法:
统计字符数量:先统计字符串中 A、S、D、W 各字符的数量。计算超出部分:确定每种字符需要移除的多余数量,如果字符数量没有超标,直接返回 0。滑动窗口:利用滑动窗口技术,动态调整子串范围,寻找满足条件的最短子串:右边界扩展窗口,直到包含所有多余字符;左边界收缩窗口,尝试找到更短的符合条件的子串。滑动窗口关键点:
窗口内的字符数量动态变化,满足条件的窗口是:count 数组中所有值小于等于 0。每次满足条件时,记录当前窗口长度并继续收缩。
import java.util.Scanner;
/**
* 这个程序用于计算游戏操作字符串中需要移除的最短子串长度,
* 使得剩余字符串中A(左移)、S(下移)、D(右移)、W(上移)四种操作的数量相等
*/
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入游戏操作字符串(只包含A,S,D,W):");
String input = scanner.next();
System.out.println("需要移除的最短子串长度为: " + minReplacementLength(input));
}
/**
* 计算需要移除的最短子串长度,使剩余字符串中A,S,D,W数量相等
* @param moves 游戏操作字符串
* @return 需要移除的最短子串长度
*/
public static int minReplacementLength(String moves) {
// 统计四种操作的数量
int[] count = new int[4]; // 索引0:A, 1:S, 2:D, 3:W
// 遍历字符串统计各操作出现次数
for (char move : moves.toCharArray()) {
int index = getIndex(move);
if (index != -1) { // 忽略无效字符
count[index]++;
}
}
// 计算每种操作的目标数量(总长度/4)
int avg = moves.length() / 4;
// 计算需要移除的多余操作数量
int requiredSteps = 0;
for (int i = 0; i < 4; i++) {
count[i] -= avg; // 计算每种操作超出平均值的数量
if (count[i] > 0) {
requiredSteps += count[i]; // 累加需要移除的多余操作
} else {
count[i] = 0; // 负数表示不足,不需要移除
}
}
// 如果已经平衡,直接返回0
if (requiredSteps == 0) return 0;
// 使用滑动窗口寻找包含所有多余操作的最短子串
int left = 0, right = 0, minLength = moves.length() + 1;
while (right < moves.length()) {
// 扩展右边界,减少对应操作的数量
char current = moves.charAt(right);
int index = getIndex(current);
if (index != -1 && count[index] > 0) {
count[index]--;
}
// 当窗口包含所有多余操作时,尝试收缩左边界
while (isBalanced(count)) {
// 更新最小长度
minLength = Math.min(minLength, right - left + 1);
// 收缩左边界,恢复对应操作的数量
char leftChar = moves.charAt(left);
int leftIndex = getIndex(leftChar);
if (leftIndex != -1 && count[leftIndex] >= 0) {
count[leftIndex]++;
}
left++;
}
right++;
}
return minLength;
}
/**
* 将操作字符转换为数组索引
* @param move 操作字符
* @return 对应的数组索引(0:A, 1:S, 2:D, 3:W), 无效字符返回-1
*/
private static int getIndex(char move) {
switch (move) {
case 'A': return 0;
case 'S': return 1;
case 'D': return 2;
case 'W': return 3;
default: return -1; // 处理无效输入
}
}
/**
* 检查当前窗口是否包含所有需要移除的多余操作
* @param count 当前窗口内各操作的剩余数量
* @return 如果所有需要移除的操作数量<=0返回true,否则false
*/
private static boolean isBalanced(int[] count) {
for (int c : count) {
if (c > 0) return false;
}
return true;
}
}
C++解法
解题思路
问题描述给定一个由字符 W、A、S 和 D 组成的字符串,要求找到一个最短的子串,移除该子串后,剩余字符串中这四种字符的数量均相等。
方法
统计字符总数:首先统计字符串中每个字符 W、A、S 和 D 的数量。判断是否已经满足条件:如果所有字符的数量已经相等,直接返回 0。滑动窗口:用两个指针 left 和 right 表示窗口的左右边界。窗口右边界向右扩展,减少窗口内字符的数量。当窗口内的字符数量满足条件(即剩余字符串中所有字符的数量不超过目标值)时,尝试收缩窗口,寻找更短的子串。关键点
每次窗口内字符数量发生变化时,判断是否满足剩余字符串中字符均衡的条件。保持窗口动态变化,直到遍历整个字符串。
#include <iostream>
#include <unordered_map>
#include <algorithm>
using namespace std;
/**
* 计算最短子串长度,使移除该子串后剩余字符串中字符 'W', 'A', 'S', 'D' 的数量均相等
* @param s 输入字符串,由字符'W','A','S','D'组成
* @return 需要移除的最短子串长度
*/
int find_min_replacement(string &s) {
// 使用哈希表统计各字符出现次数
unordered_map<char, int> count = {{'W',0}, {'A',0}, {'S',0}, {'D',0}};
int n = s.size();
// 计算每个字符的目标数量(总长度/4)
int required = n / 4;
// 遍历字符串,统计各字符出现次数
for (char c : s) {
count[c]++;
}
// 检查是否已经满足均衡条件
if (count['W'] == required && count['A'] == required &&
count['S'] == required && count['D'] == required) {
return 0; // 无需移除任何子串
}
// 滑动窗口初始化
int left = 0, right = 0;
int min_len = n; // 初始假设需要移除整个字符串
// 滑动窗口遍历字符串
while (right < n) {
// 窗口右边界扩展,将当前字符从剩余字符中减去
count[s[right]]--;
// 当剩余字符满足均衡条件时
while (left <= right &&
count['W'] <= required &&
count['A'] <= required &&
count['S'] <= required &&
count['D'] <= required) {
// 更新最小窗口长度
min_len = min(min_len, right - left + 1);
// 收缩左边界,恢复移除的字符计数
count[s[left]]++;
left++;
}
right++; // 继续扩展右边界
}
return min_len;
}
int main() {
string s;
cout << "请输入由W,A,S,D组成的字符串:";
cin >> s; // 获取用户输入
// 验证输入有效性
for (char c : s) {
if (c != 'W' && c != 'A' && c != 'S' && c != 'D') {
cout << "输入包含非法字符!" << endl;
return 1;
}
}
// 检查字符串长度是否为4的倍数
if (s.size() % 4 != 0) {
cout << "字符串长度必须是4的倍数!" << endl;
return 1;
}
cout << "需要移除的最短子串长度为:" << find_min_replacement(s) << endl;
return 0;
}
1063

被折叠的 条评论
为什么被折叠?



