leetcode#552. Student Attendance Record II

来源:互联网 发布:淘宝5金冠店铺有哪些 编辑:程序博客网 时间:2024/06/05 11:31

题目

Given a positive integer n, return the number of all possible attendance records with length n, which will be regarded as rewardable. The answer may be very large, return it after mod 109 + 7.

A student attendance record is a string that only contains the following three characters:

  1. ‘A’ : Absent.
  2. ‘L’ : Late.
  3. ‘P’ : Present.

A record is regarded as rewardable if it doesn’t contain more than one 'A' (absent) or more than two continuous 'L' (late).

Example 1:

Input: n = 2Output: 8 Explanation:There are 8 records with length 2 will be regarded as rewardable:"PP" , "AP", "PA", "LP", "PL", "AL", "LA", "LL"Only "AA" won't be regarded as rewardable owing to more than one absent times. 

Note: The value of n won’t exceed 100,000.

思路一

思路一及思路二都是参考leetcode别的用户的答案

原文思路

dp[i]the number of all possible attendance (without 'A') records with length i :end with "P": dp[i-1]end with "PL": dp[i-2]end with "PLL": dp[i-3]end with "LLL": is not allowedso dp[i] = dp[i-1] + dp[i-2] + dp[i-3]the number of all possible attendance (with 'A') records with length n:∑dp[i] *dp[n-1-i] i = 0,1,...,n-1Time Complexity O(n)Space Complexity O(n)

先将A排除在外, 只考虑P和L。那么此时就只有以下3种情况:

  1. 以P结尾
  2. 以PL结尾
  3. 以PLL结尾

所有的合法组合中,肯定符合这三种情况的一种。这样我们就可以进行递归操作了,去除这些结尾后,剩下的字串也是符合这三种情况的,再将结尾去掉,判断剩下的字串…
用公式来表达,就是:
dp[i] = dp[i-1] + dp[i-2] + dp[i-3]
根据这个公式,我们就可以递归的算出n个不含A的字符串的所有合法组合了。接下来我们只要把A加进去,就是所有合法的组合了。
一开始,我以为只要在dp[n - 1]的基础上把A加进去就可以了,此时一共有n个位置可以让A放,所以带A的合法组合一共有dp[n - 1] * n个。事实上,当n = 3时,的确是这么回事,但是当n = 4时我发现算出来结果比正常结果小了,这就说明有情况漏算了。思前想后,还是觉得理论没错呀,最后就干脆拿手把情况一个个全列出来了,终于发现了问题所在。在n = 4时,LALL和LLAL都是合法的组合,这按我之前的思路因该是A插在LLL中间生成的,但是,LLL在n = 3时并不是合法的组合,所以单纯的将dp[n - 1]的合法情况乘以n是不足以算出所有情况的。
这时我又去看了看原思路作者的说明:
∑dp[i] *dp[n-1-i] i = 0,1,...,n-1
What the fxxk is this!
点进这个讨论,看了看详情,果然有人也在问,也有人回答了,看了后终于懂了。
For n=3, it becomes dp0dp2+dp1dp1+dp2dp0, When you place A at pos1, then P&L can be placed in dp0dp2 ways.看似没有直接解释,但倒是点通了我。
当n = 3时,假若A放在pos1的位置上,那么P和L有dp[0] * dp[2]种摆法。同理,A放在pos2的位置上时,左边还有俩空,所以有dp[2]种摆法,右边没位置了,所以有dp[0]个摆法,此时一共有dp[2] * dp[0]个摆法…将A所有的位置遍历一遍后,就将有A的所有组合算出来了。
因此最后,根据这个思路,我的代码如下

代码一

class Solution(object):    def checkRecord(self, n):        """        :type n: int        :rtype: int        """        if n == 0:            return 0        if n == 1:            return 3        if n == 2:            return 8        MAX = 1000000007        dp = [1, 2, 4]#这里是手动计算出的n = 0,1,2时的dp值        i = 3        while i < n:            dp.append((dp[i - 1] + dp[i - 2] + dp[i - 3]) % MAX)            i += 1        result = (dp[n - 1] + dp[i - 2] + dp[i - 3]) % MAX        for i in range(n):            result += dp[i] * dp[n - i - 1] % MAX            result %= MAX        return result

思路一结论

这个解题思路很符合DP的思想,我不用管所有的情况,我只要知道当前的情况,并且知道当前情况如何退出下一个情况即可,这个思路值得学习。

思路二

来源于另一个作者,他的描述是这样的:

At time t where every report is length t, Let a, b, c be sequence types without an 'A' ending in N, NL, LL; and d,e,f be sequence types with an 'A' ending in N, NL, LL. (Here, N will denote a non-'L' character.) These types are disjoint, and exhaustive (their union is the set of all valid reports.) At the beginning when t = 1, a = b = d = 1 and we should compute N-1 more steps.From a sequence of type a, b, c, we can write an 'A' to give us a sequence of type d, or a 'P' to give us a sequence of type a. From a sequence of type d, e, f, we can write a 'P' to give us a sequence of type d. From a sequence of type a, b, d, e, we can write an 'L' to give a sequence of (respectively) b, c, e, f. These are all the letters we could write in any situation. Working backwards, we can get the sums for a,b,c,d,e,f written below.

作者代码如下:

def checkRecord(self, N):    MOD = 10**9 + 7    a = b = d = 1    c = e = f = 0    for _ in xrange(N-1):        a, b, c, d, e, f = (a+b+c)%MOD, a, b, (a+b+c+d+e+f)%MOD, d, e    return (a+b+c+d+e+f)%MOD

读完后反复推敲了好久,这位作者的意思应该是这样的:
对于一个长度为t的序列,我们设a,b,c为不含A且以P,PL,PLL结尾的序列;设e,d,f为包含A且以N,NL,NLL结尾的序列。此处的N表示非L字符,即A或者P。这几种情况互不相交,并集又是全集。
此时,对于a,b,c来说,在末尾加个字符A进去,则它们就会变成d,或者在末尾加个字符P进去,则它们就会变成a;对于d,e,f来说,在末尾加个字符P,它们就会变成d;对于a,b,d,e来说,在末尾加个L,它们分别会变成b,c,e,f;这就是所有可能的情况了,最后a到e的和就是最后的结果。

思路二结论

一开始觉得作者描述似乎有道理,但又觉得似乎不是这么回事,尤其是看作者原文描述的时候,描述的挺不详细的…思考了许多种他可能的描述的意思,最后终于确定了作者的意思应该就是我转述的这种,这几种状态的转换也终于对上了…
其实这是一种有限状态自动机的思想,当然也是DP的思想,状态就这6种,来回转换也就这6种,最后把6种状态的结果加在一起就好了。这个思路的精髓是如何找到这几种状态,估计这也是多做多练才能想得到吧。

最后的总结

这道题是hard难度的,我自己就没有想到很好的办法来解决,但是看到其中两个答案后,就觉得惊艳了,学习到了不少新思维方式。虽然这题花了两天时间来解决,但是我感觉比一天做两道水题有意思多了,以后还是多做做这些有意思的题,水题还是无聊时刷刷吧。

原创粉丝点击