中行软开 11.3编程测评第4题 题解

题干:给定一串小写字符,每一个字符都可以转换成字母表中相邻的其他字符,但是a、z之间无法直接转换,求最少转换次数,使该字符串不存在两个辅音字母相邻

思路:读完题定下的基本认知是,元音字母将字符串分割为多个全为辅音字母的字符串,每一个辅音字符串的转换都是互不相干的独立问题,所以问题简化成,对一个全为辅音字母的字符串,求最少转换次数,使该字符串不存在两个辅音字母相邻

首先想到的思路就是动态规划,但是因为前三题过于简单,所以考虑寻找更简单的解法,尝试了135...246...这种交替转换,提交上去a了50%,想到对于"zbbz"的样例,应该是2、3位直接转换成a是最优解,交替转换的思路不可取,于是重新思考动规解法。

状态定义:

dp1:在第n位转换成元音字母的情况下,前n个字符组成的字符串,满足无连续辅音字母所需转换的最少次数

dp2:前n个字符组成的字符串,满足无连续辅音所需转换的最少次数

dp2就是待求的问题。

状态转移方程:

对于dp1,如果第n位变成了元音字母,那么问题就等价于求前n-1个辅音字符串,满足无连续辅音所需转换的最少次数,恰好是dp2[n-1],则dp1=dp2[n-1]+count(n)

对于dp2,

情况1:如果最优解中第n-1位变成了元音字母,则最优解中第n位一定无需转换,最优解就是dp1[n-1]

情况2:如果最优解中第n-1位不变且第n-2位变成了元音字母,则最优解中第n位一定需要转换,最优解是dp1[n-2]+count(n)

情况3:最优解中第n-1位不变,且第n-2位不变,这种情况显然不存在,因为出现了连续两个辅音字母

状态转移方程如下:

// count(n) 是第n个辅音字母转换成元音字母所需转换的最小次数
dp1[n] = dp2[n-1] + count(n)
dp2[n] = min(dp1[n-1], dp1[n-2]+count(n))

状态初始化:

dp1[0] = count(0), dp1[1] = count(1)
dp2[0] = 0, dp2[1] = min(dp1[0], dp1[1])

代码实现:

def get_distances() -> dict[str: int]:
    """求每一个辅音字母转换为元音字母所需的最小转换次数,常数级时间复杂度"""
    distances = dict()
    for c in 'abcdefghijklmnopqrstuvwxyz':
        if c in vowel:
            continue
        distances[c] = 26
        for v in vowel:
            distances[c] = min(distances[c], abs(ord(v) - ord(c)))
    return distances


vowel = {'a', 'e', 'i', 'o', 'u'}
dis = get_distances()


def main(s: str) -> int:
    res = 0
    seg = ''
    # 分割出每一个连续的辅音字符串,独立求解,加和得到最终结果
    for c in s:
        if c in vowel:
            if len(seg) >= 2:
                res += count(seg)
            seg = ''
            continue
        seg += c
    
    if len(seg) >= 2:
        return res + count(seg)
    return res


def count(s: str) -> int:
    dp1 = [0] * len(s)
    dp2 = [0] * len(s)
    dp1[0] = dis[s[0]]
    dp1[1] = dis[s[1]]
    dp2[1] = min(dp1[0], dp1[1])
    for i in range(2, len(s)):
        # 以下是状态转移方程,可以进行存储优化
        dp1[i] = dp2[i-1] + dis[s[i]]
        dp2[i] = min(dp1[i-1], dp1[i-2] + dis[s[i]])

    return dp2[-1]


# def count(s: str) -> int:
#     """存储优化版本"""
#     a, b = dis[s[0]], dis[s[1]]
#     c = min(a, b)
#     for i in range(2, len(s)):
#         c_new = min(b, a + dis[s[i]])
#         a, b = b, c + dis[s[i]]
#         c = c_new
#     return c


print(main('azbbzabcdabbb'))

复杂度:

时间复杂度:O(n)

空间复杂度:O(n),存储优化版本的复杂度是O(1)

#中国银行##编程测评##题解#
全部评论
大佬
1 回复
分享
发布于 2023-11-06 22:01 天津
大佬,其他3题怎么不见了😭😭
1 回复
分享
发布于 2023-11-29 10:37 山东
滴滴
校招火热招聘中
官网直投

相关推荐

 中国银行编程能力测评分享中行编程能力测评使用的是牛客平台,严格双机位拍摄模式,120min 4 道编程题(19:00 - 21:00),编程语言不限,提交代码为 LeetCode 模式~⭐ 题目大概如下 ⬇️(个人答案仅供参考,欢迎提出其他解法~1. 开发网站时,对于密码有一些规则:    1. 密码长度至少为 10   2. 只能包含大小写字母 和 数字   3. 至少出现 大写字母、小写字母、数字 这3种类型里的 2 种给定一个密码 mypassword,判断密码是否符合规则?符合返回 true,否则返回 false。2. 小王有一个银行卡密码(存在字符串中),只包含数字和大写字母,现知道密码的规则如下:   1. 必须是一串连续的数字   2. 如果数字之间有”BAC“字符串的话,可以消除掉   3. 数字可能会很大现需要从字符串中找到密码,找到的话输出符合规则的最大的数,如果字符串中没有数字的话返回 -1。3. 在数据库中,为了方便存储 ip 地址,一般会把 ip 地址转化为一个十进制数字进行存储。现需要把一个十进制的数字变回为 ip 地址的形式,规则如下:   1. 首先把十进制的数字变成十六进制   2. 每 2 位十六进制为一段,将其变成十进制,再加上点'.',填入到 ip 结果中。现对于一个数字,需要输出其对应的 ip 地址字符串。如果 ip 地址非法,则输出 “invalid”。4. 假设有很多不同面额的货币,每个货币只可以使用1次,但可以用不同面额的货币加在一起来购买大面额的商品,但是不想找零,因此对于一些金额的商品,无法进行购买。比如有 1、2、5 元的货币,我可以购买 3 元的商品(1 + 2),但是由于 5 元无法找零,因此无法购买 4 元的商品。现给定一个长度为 n 的正整数数组 money,分别表示 n 个货币的金额,求出无法购买的商品最小价格。#中国银行笔试# #银行科技岗# #信息科技# #编程能力测评# #笔经# #中国银行# #银行校招# #中行笔试# #中行#
投递中国银行等公司7个岗位
点赞 评论 收藏
转发
16 51 评论
分享
牛客网
牛客企业服务