已获得原公众号的授权转载
在第一人称射击游戏中,玩家通过键盘的 A
、S
、D
、W
四个按键控制游戏人物分别向左、向后、向右、向前进行移动,从而完成走位。
假设玩家每按动一次键盘,游戏人物会向某个方向移动一步,如果玩家在操作一定次数的键盘并且各个方向的步数相同时,此时游戏人物必定会回到原点,则称此次走位为完美走位。
现给定玩家的走位(例如:ASDA
),请通过更换其中一段连续走位的方式使得原走位能够变成一个完美走位。其中待更换的连续走位可以是相同长度的任何走位。
请返回待更换的连续走位的最小可能长度。若果原走位本身是一个完美走位,则返回 0
。
输入为由键盘字母表示的走位s
,例如:ASDA
输出为待更换的连续走位的最小可能长度
1 ≤ s.length ≤ 10^5
s.length
是 4
的倍数s
中只含有 A
, S
, D
, W
四种字符ASDW
0
已经是完美走位了。
AASW
1
需要把一个 A
更换成 D
,这样可以得到 ADSW
或者 DASW
。
AAAA
3
可以替换后 3
个 A
,得到 ASDW
。
AAAAADDD
4
“注意,本题和LC76. 最小覆盖子串几乎完全一致。重点在于如何将原问题转化为覆盖子串的问题。
”
题目有两个重要条件:
A
、S
、D
、W
四种字符出现次数相等的字符串s.length
是 4
的倍数对于长度为len(s)
的原字符串s
来说,为了使其转变为一个完美走位字符串,其中A
、S
、D
、W
四种字符出现次数应该均为 num = len(s) // 4
。
原字符串s
中各个字符出现的次数可以用哈希表cnt_s = Counter(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
)的最短子串。需要覆盖的子串中所出现的字符以及次数,可以用另一个哈希表cnt_sub
储存。
那么这个问题就和LC76. 最小覆盖子串完全一致了。上述逻辑整理为代码即
num = len(s) // 4
cnt_s = Counter(s)
cnt_sub = {k: v-num for k, v in cnt_s.items() if v > num}
# 题目:2023Q1A-完美走位
# 分值:100
# 作者:许老师 && 吴师兄学算法
# 算法:不定滑窗
# 代码看不懂的地方,请直接在群上提问
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)
时间复杂度:O(N)
。仅需一次遍历整个字符串s
。
空间复杂度:O(1)
。只有4
种字符,哈希表所占用空间为常数级别空间。
<END> 程序员专属T恤
商品直购链接 👇
推荐阅读:
文章引用微信公众号"脚本之家",如有侵权,请联系管理员删除!