Python求解最少的打字次数打出对应的字符串
这是京东的一个笔试题目
给定一个字符串,键盘按键Caps Lock可以切换大小写模式,在大写模式下,若想输入小写字母,可以shift+字母,同样在小写模式下,若想输入大写字母可以shift+字母,给定一个字符串求最小的点按键击次数。注意:shift+字母算两次按键点击,默认初始状态是小写模式。
暴力破解法
这道题目麻烦在哪里?他有什么需要我们注意的点
无法确定输入的所处的锁定状态
你不能确定每次输入的时候你所处的状态,你是在大写状态输入还是在小写状态输入
面临选择:shift还是CapsLk
你在一个状态打字的时候遇到不同状态的字符该如何处理?你可以选择shift 或者选择CapsLk,那么做出哪一个选择对于当前来说是最优解?
如果不知道当前的下一个字符其实做什么选择都一样,因为都是两次按键,但是如果下一个字符是确定的,当前的最优解只有一种:
当前状态下的最优按键策略:
同状态输入
即是你在大写锁定下打出大写字母,或者小写锁定下打出小写字母,这种模式下不用选择,默认只需按一次键
异状态输入
当你在大写锁定下打小写字母或者小写模式下打出大写字母:这时你面临上面说的两种选择。这时候的最优解取决于你要打的是不是连续的大写,或者连续的小写。
假设当前是大写锁定模式:
你输入到了当前位置为a:……aA……
这时候呢我们应该怎么选择,用CapsLk还是Shift键?
1.如果用CapsLk本次打出a按键要2次 ,大小写锁定模式被改为小写,下一个字母打出A你肯定也要按键2次。
2.如果本次用shift打出a,本次按键要两次,大小写没有被改变,还是大写锁定,下一次打出A,你只需要一次按键。
def minimum_typing(s1):
count = 0
# 默认开始输入时键盘为小写
flag = True
n = len(s1)
# 忽略字符串最后一位直接计算次数
for i in range(n - 1):
if flag: # 当前的输入模式为小写锁定
if s1[i].islower(): # 小写锁定下打出小写字母,只用一次按键
count += 1
if s1[i].isupper(): # 小写锁定下要输入的当前字母是大写字母,这时有两种输入模式:
# 按shift+字母或者CapsLk直接切换大小写 不管怎么输入这一次都会按两个键
if s1[i + 1].islower(): # 下个字母还是小写按键次数+2,输入模式不会切换
count += 2
if s1[i + 1].isupper(): # 小写模式下当前字母和下个字母都是大写,切换大小写锁定
count += 2 # 按键次数加2
flag = False # 切换输入模式
continue # 这个很重要 要返回继续判断,不能直接往下面执行
if not flag: # 当前输入模式为大写锁定,判断逻辑和上面一样
if s1[i].isupper(): # 大写输入大写次数+1
count += 1
if s1[i].islower(): # 大写输入小写
if s1[i + 1].isupper(): # 不是连续的小写 直接shift打字次数+2 模式不切换
count += 2
if s1[i + 1].islower(): # 连续小写 切换模式
count += 2
flag = True
continue
if flag ^ s1[-1].islower(): # 最后一个字母和当前输入模式不同
count += 2
else: # 最后一个字母和当前输入模式相同
count += 1
return count
动态规划解法
用动态规划的思想来解题,我们首先需要一个两行的二维数组来记录处于每个字符的打字次数第一行的数据用来记录输入到此字符输入状态是小写锁定状态的数据,第二行的数据用来记录输入到此字符输入状态是小写锁定状态的数据:
我们可以得到一个二位的状态数组这个数组有两行
第一行表示我们处在小写状态,第二行表示我们处在大写状态 数组中的数据表示我们输入到第i个字符所用的总的按键次数。
假设你正在输入第i个字符我们怎么得到dp[0][i]的数据和dp[1][i]的数据呢?
我们先得知道增输入的第i个字符是大写还是小写,先假设输入的是大写的字符吧:
首先我得要知道dp[0][i]即是你打出这个字符之后你处于小写状态的总次数,dp[1][i]即是你打出这个字符之后打字状态为大写的总次数:要打出一个大写A
dp[0][i]=dp[0][i-1]+2 或者=dp[1][i-1]+2
dp[1][i]=dp[1][i-1]+1 或者=dp[0][i-1]+2
def minimum_typing_1(s1):
# 初始化状态数组
dp = [[0 for _ in s1], [0 for _ in s1]]
# 初始默认状态为小写锁定
dp[0][0] = 1 if s1[0].islower() else 2
# 表示状态改变了 输入第一个字符输入锁定变为大写
dp[1][0] = 2
n = len(s1)
for i in range(1,n):
if s1[i].islower():
# 选择最优解
dp[0][i] = dp[0][i-1]+1 # 这里为什么没有使用dp[0][i] = min(dp[0][i-1]+1,dp[1][i-1]+2)
#和上一种解法的选择一样,同种状态输入匹配的字符永远是最优的。
dp[1][i] = min(dp[0][i-1]+2,dp[1][i-1]+2)
else:
dp[0][i] = min(dp[0][i-1]+2,dp[1][i-1]+2)
dp[1][i] = dp[1][i-1]+1
return min(dp[0][n-1],dp[1][n-1])