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)