动态规划:力扣552. 学生出勤记录 II

1、题目描述:

在这里插入图片描述

2、题解:

方法1:暴力法,超时
思路:

用gen(s,n)生成所有'A''P','L'所组成的字符串,然后用551题的方法判断是否可以奖励
对于gen(s,n),在递归函数的每一层分别在输入字符串后面加上'A''P','L',剩余长度n - 1,分别进行递归
其中,每个gen()前面的if语句,有剪枝的作用

代码实现:

class Solution:
    def checkRecord(self, n):
        #暴力法,超时
        # 用gen(s,n)生成所有'A','P','L'所组成的字符串,然后用551题的方法判断是否可以奖励
        #对于gen(s,n),在递归函数的每一层分别在输入字符串后面加上'A','P','L',剩余长度n - 1,分别进行递归
        self.count = 0
        self.gen('',n)
        return self.count
    def gen(self,s,n):
        if n == 0 and self.check(s):
            self.count = (self.count + 1) % (pow(10,9) + 7)
        elif n > 0:
            if self.check(s + 'A'):#剪枝
                self.gen(s + 'A',n-1)
            if self.check(s + 'P'):#剪枝
                self.gen(s + 'P',n - 1)
            if self.check(s + 'L'):#剪枝
                self.gen(s + 'L',n - 1)
    def check(self,s):
        if s.count('A') < 2 and 'LLL' not in s:
            return True
        else:
            return False

方法2:递归,超时
思路:

思路:
先不考虑以'A'结尾字符串,先只考虑以'L','P'结尾的可奖励字符串;得到后,再处理字符'A'。
在求dp()时,是递归:
    递归结束dp[0],dp[1],dp[2],dp[3]
    也就是求以'L','P'结尾的可奖励字符串时,dp[i] = 2 * dp[i-1] - dp[i-4] ,解释:该位置的可奖励的字符串数
    等于下一个位置以'P'结尾的可奖励的字符串数+(下一个位置以'L'结尾的可奖励的字符串数-'LLL'结尾的字符串数)
处理字符'A':下面两种情况的加和
    1)如果不包括'A',那么可奖励的字符串数组就是dp[n]2)否则,如果包括'A',就在之前得到的字符串中(n-1个字符,留一个位置给'A')
    插入 'A'即可,也就是res += dp[i] * dp[n - 1 - i]

单纯递归会超时
代码如下:

class Solution:
    def checkRecord(self, n):
        # 递归
        mod = 1000000007
        temp = [0] * (n + 1)
        for i in range(n + 1):
            temp[i] = self.dp(i)
        res = temp[n]
        for i in range(n):
            res += temp[i] * temp[n - 1 - i] % mod
        return res % mod

    def dp(self, n):
        mod = 1000000007
        if n == 0: return 1
        if n == 1: return 2
        if n == 2: return 4
        if n == 3: return 7
        return (2 * self.dp(n - 1) - self.dp(n - 4)) % mod

方法3:动态规划
动态规划问题,弄清楚三点:

1、重复子问题;
2、最优子结构;
3、无后效性。

动态规划:

1、状态定义;
2、状态转移方程;
3、初始化;base case
4、输出;
5、思考状态压缩。

可以用递归去求,但是会存在重叠子问题,加个备忘录可以解决重复问题。
递归+备忘录
也就是在方法2上加个备忘录,可以AC

class Solution:
    def checkRecord(self, n):
        # 递归+备忘录
        mod = 1000000007
        self.temp = [0] * (n + 1)
        for i in range(n + 1):
            self.temp[i] = self.dp(i)
        res = self.temp[n]
        for i in range(n):
            res += self.temp[i] * self.temp[n - 1 - i] % mod
        return res % mod

    def dp(self, n):
        mod = 1000000007
        if n == 0: return 1
        if n == 1: return 2
        if n == 2: return 4
        if n == 3: return 7
        if self.temp[n]:
            return self.temp[n]
        self.temp[n] = (2 * self.dp(n - 1) - self.dp(n - 4)) % mod
        return self.temp[n]

动态规划:

状态定义:dp[n] 为不包括'A',以'P''L'结尾的可奖励的字符串数
状态转移方程:dp[n] = 2 * dp[n-1] - dp[n - 4] = dp[n - 1] + (dp[n-1] - dp[n-4]),
该位置的可奖励的字符串数等于下一个位置以'P'结尾的可奖励的字符串数+(下一个位置以'L'结尾的可奖励的字符串数-'LLL'结尾的字符串数)
base case:
    dp[0], dp[1], dp[2], dp[3] = 1, 2, 4, 7
    要注意,此时求的还没包括'A',dp = [0] * (4 if n <= 3 else n + 1)
求出后再考虑字符串'A'的情况
class Solution:
    def checkRecord(self, n):
        #动态规划
        mod = 1000000007
        dp = [0] * (4 if n <= 3 else n + 1)
        dp[0],dp[1],dp[2],dp[3] = 1,2,4,7
        for i in range(4,n + 1):
            dp[i] = (2 * dp[i-1] - dp[i - 4]) % mod
        res = dp[n]
        for i in range(n):
            res += dp[i] * dp[n-1-i] % mod
        return res % mod

当然还可以进行状态压缩,以后想明白补上

3、复杂度分析:

方法1:
时间复杂度:O(3^N),需要搜索所有的组合,当然,其中有剪枝(if语句)
空间复杂度:O(N^N),递归树的深度最多为N层,每个节点包含长度为O(N)的字符串
方法2:
时间复杂度:O(2^N),函数dp()最多需要花费时间
空间复杂度:O(N),temp数组的大小为N
方法3:
时间复杂度:O(N)
空间复杂度:O(N)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值