题解
题目描述
在第一人称射击游戏中,玩家通过键盘的 A、S、D、W 四个按键控制游戏人物分别向左、向后、向右、向前进行移动,从而完成走位。
假设玩家每按动一次键盘,游戏任务会向某个方向移动一步。如果玩家操作一定次数的键盘后,各个方向的步数相同,此时游戏任务必定会回到原点,则称此次走位为完美走位。
现给定玩家的走位(例如:ASDA),请通过更换其中一段连续走位的方式,使得原走位能够变成一个完美走位。其中待更换的连续走位可以是相同长度的任何走位。
请返回待更换的连续走位的最小可能长度。
如果原走位本身是一个完美走位,则返回 0。
思路
要使走位成为完美走位,各个方向的步数必须相同。假设走位长度为 n,由于 n 是 4 的倍数,每个方向的目标步数为 n/4。
- 统计当前各方向的步数:
统计 A,S,D,W 各自的出现次数。 - 判断是否已经是完美走位:
如果所有方向的步数都等于 n/4,则返回 0。 - 计算需要调整的步数:
对于每个方向,计算超出目标步数的部分(即需要减少的步数)。 - 使用滑动窗口寻找最小替换区间:
- 使用双指针(滑动窗口)从字符串左端开始,逐步扩大右端。
- 在窗口内,减少相应方向的超出步数。
- 当所有超出步数均被窗口覆盖(即窗口内的字符可以替换为所需的方向),记录窗口长度并尝试缩小窗口以找到最小长度。
代码分析
- 统计各方向的步数:遍历字符串,统计 A,S,D,W 的数量。
- 计算多余步数:对于每个方向,计算当前步数与目标步数 n/4 的差,如果大于 0,则记录为多余步数。
- 滑动窗口:
- 初始化左指针为 0,右指针遍历整个字符串。
- 在每次移动右指针时,若当前字符是多余的方向,则减少相应的多余步数。
- 当所有多余步数均被窗口覆盖时,尝试移动左指针以缩小窗口,直到不满足条件为止。
- 记录所有满足条件的窗口长度,取最小值。
python
import sys
from collections import defaultdict
def main():
s = sys.stdin.read().strip()
n = len(s)
target = n // 4
count = defaultdict(int)
for c in s:
count[c] += 1
# 计算多余的步数
surplus = {}
for c in 'ASDW':
if count[c] > target:
surplus[c] = count[c] - target
# 如果没有多余步数,则已经是完美走位
if not surplus:
print(0)
return
min_len = n
window = defaultdict(int)
left = 0
formed = 0
required = len(surplus)
for right in range(n):
c = s[right]
if c in surplus:
window[c] += 1
if window[c] == surplus[c]:
formed += 1
while formed == required:
min_len = min(min_len, right - left + 1)
d = s[left]
if d in surplus:
window[d] -= 1
if window[d] < surplus[d]:
formed -= 1
left += 1
print(min_len)
if __name__ == "__main__":
main()
java
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String s = sc.next();
int n = s.length();
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> surplus = new HashMap<>();
for(char c : new char[]{'A','S','D','W'}){
if(count.getOrDefault(c, 0) > target){
surplus.put(c, count.get(c) - target);
}
}
// 如果没有多余步数,则已经是完美走位
if(surplus.isEmpty()){
System.out.println(0);
return;
}
int minLen = n;
Map<Character, Integer> window = new HashMap<>();
int left = 0;
int formed = 0;
int required = surplus.size();
for(int right = 0; right < n; right++){
char c = s.charAt(right);
if(surplus.containsKey(c)){
window.put(c, window.getOrDefault(c, 0) + 1);
if(window.get(c).intValue() == surplus.get(c).intValue()){
formed++;
}
}
while(formed == required){
minLen = Math.min(minLen, right - left + 1);
char d = s.charAt(left);
if(surplus.containsKey(d)){
window.put(d, window.get(d) - 1);
if(window.get(d) < surplus.get(d)){
formed--;
}
}
left++;
}
}
System.out.println(minLen);
}
}
cpp
#include <bits/stdc++.h>
using namespace std;
int main(){
string s;
cin >> s;
int n = s.length();
int target = n / 4;
unordered_map<char, int> count;
for(char c : s) count[c]++;
// 计算多余的步数
unordered_map<char, int> surplus;
for(auto &[k, v] : count){
if(v > target){
surplus[k] = v - target;
}
}
// 如果没有多余步数,则已经是完美走位
if(surplus.empty()){
cout << 0;
return 0;
}
// 使用滑动窗口寻找最小长度
int minLen = n;
unordered_map<char, int> window;
int left = 0;
int required = surplus.size();
int formed = 0;
for(int right = 0; right < n; right++){
char c = s[right];
if(surplus.find(c) != surplus.end()){
window[c]++;
if(window[c] == surplus[c]){
formed++;
}
}
// 当所有多余的方向都在窗口中被覆盖
while(formed == required){
minLen = min(minLen, right - left + 1);
char d = s[left];
if(surplus.find(d) != surplus.end()){
window[d]--;
if(window[d] < surplus[d]){
formed--;
}
}
left++;
}
}
cout << minLen;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。