552 学生出勤记录 II(递推-动态规划-枚举技巧)

示例 1:

输入: n = 2

输出: 8

解释:

有8个长度为2的记录将被视为可奖励:

“PP” , “AP”, “PA”, “LP”, “PL”, “AL”, “LA”, “LL”

只有"AA"不会被视为可奖励,因为缺勤次数超过一次。

注意:n 的值不会超过100000。

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/student-attendance-record-ii

2. 思路分析:

分析题目可以知道我们可以在短的字符串后面尝试添加字母A,L,P,判断是否可以构成更长的满足要求的字符串…直到最终构成长度为n的字符串,将所有长度为n的合法方案累加起来就是答案。这个过程其实是一个递推的过程,由上一个状态递推到当前的状态,一直到目标状态,所以我们可以使用动态规划递推的思路解决(想到递推的思路很重要)。使用dp解决首先需要考虑状态表示,也即需要使用到哪些值(方便后面的状态转移),可以发现我们需要用到的有当前的字符个数,当前字母A的数目,当前连续字母L的数目,所以我们可以定义三维的数组或者列表,因为使用的是python语言所以可以定义为三维的列表,其中dp[i][j][k]表示当前字符个数为i,字母A的个数为j,连续字母L的数目为k的所有方案,对于这种常规的递推的状态转移,有两种常见的dp转移的方法,也即对应的状态计算,第一种是由上一个可能的状态集合转移到当前的状态,也即计算由上一个状态转移到当前状态的方案数目,可以发现dp[i][j][k]由上一个状态集合转移到当前的状态的时候需要判断很多的情况所以比较难写。第二种方法是我们可以考虑枚举下一个状态,也即枚举下一个位置可能填什么才能够从当前的状态转移到下一个可能的状态,这种枚举方式会更好处理,我们只需要判断下一个位置填字母A,L,P是否合法,也即是否可以由当前的状态转移到下一个状态。使用三层循环枚举即可,第一层循环当前字符的个数,第二层循环表示字母A的个数,第三层循环表示连续字母L的个数,在循环中判断当前状态的下一个位置填字母A,L对应的j,k的变量是否合法,在转移到下一个状态的时候需要累加上当前状态的方案数目即可。最后遍历一下字符个数为n的字母A和字母L合法的状态数目,累加起来就是答案,我们可以采用第二种枚举下一个位置可能填的字母情况的枚举方法这样会更容易处理。主要是题目中蕴含的递推的思想。

3. 代码如下:

发现python代码与java代码差别太大了,python差不多8s,java只需要230ms左右。

python代码(8s)

class Solution:

def checkRecord(self, n: int) -> int:

声明一个(n + 1) * 2 * 3的三维列表, dp[i][j][k]表示i个字符, j个字母A, k个连续的字母L

dp = [[[0] * 3 for i in range(2)] for j in range(n + 1)]

初始状态表示当前有0个字母, 0个A, 0个连续的字母L的方案数目为1

dp[0][0][0] = 1

mod = 10 ** 9 + 7

for i in range(n):

第二层表示字母A的数目

for j in range(2):

第三层表示连续字母L的数目

for k in range(3):

枚举的是下一个位置, 因为求解的是方案数目, 所以需要累加上当前状态的dp值

判断下一个位置填字母A的情况

if j == 0: dp[i + 1][j + 1][0] = (dp[i + 1][j + 1][0] + dp[i][j][k]) % mod

判断下一个位置为L的字母的情况

if k + 1 <= 2: dp[i + 1][j][k + 1] = (dp[i + 1][j][k + 1] + dp[i][j][k]) % mod

下一个位置为字母P一定合法所以直接计算即可

dp[i + 1][j][0] = (dp[i + 1][j][0] + dp[i][j][k]) % mod

res = 0

计算有n个字母所有的合法方案, 包含合法的字母A和字母L的情况

for j in range(2):

for k in range(3):

res = (res + dp[n][j][k]) % mod

return res

java代码(232ms)

public class Solution {

public int checkRecord(int n) {

int dp[][][] = new int[n + 1][2][3];

dp[0][0][0] = 1;

int mod = 1000000007;

for (int i = 0; i < n; ++i){

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档

祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!

感谢大家的支持!!

image.png

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
mg-ZpXrssiG-1713055726364)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 11
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值