leetcode#552. Student Attendance Record II

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013450405/article/details/75051948

题目

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 = 2
Output: 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 allowed
so 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-1

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

阅读更多
换一批

Student Record Inquiry System(学生成绩查询系统)开发计划书

12-27

rn这个是一个计划项目,欢迎大家提提建议。rn欢迎大家说说自己心目中的学生成绩查询系统是什么样的?rn:)rnrnrnStudent Record Inquiry System(学生成绩查询系统)开发计划书rnrnrnrnrnrn主要功能:rnrn系统客户端:1.普通注册用户登陆,只能浏览到学校的一些新闻,公告之类的信息。rn2.普通注册用户可以申请进入某人班级,经班级指导员批准,可以查询自己的个科目成绩,学期评语,以及相关的自身个人信息。(如果指导员设置班级的每一个学生都可以浏览其他学生的成绩,那么该学生还可以查看班级的其他学生信息。)rn3.高级用户权限,该类用户的面向对象是学校校长之类的,可以查看系/级/班的所有学生成绩,档案,学期评语等等。rn4.用户查看班级,年级时可以按照学生的学号/各科成绩/总成绩等等进行有条件排序查看。rnrn系统教师端:1.科任教师注册后等待班级指导员开通权限后,科任教师可填加学生该科的成绩。rn2.班级指导员可以填加/修改/删除该班该学期的科目。rn3.班级指导员可以编辑/删除该班各个学生的学期评语和各科成绩。rn4.班级指导员可以查询该班各个学生的成绩和评语。rn5.班级指导员可以锁定学生资料,避免学生编辑个人相关信息。rn6.班级指导员可以对学生进行编辑/删除管理。rn7.级长,继承所有班级指导员地权限,但是其管理范围为该年级以内。rn8.校长/主任,继承全校所有班级指导员的权限。rnrn后台管理 :1.可以对各班级的科目/科任教师进行相关的操作(填加/编辑/删除)。rn2.填加/编辑/删除各班班级/年级,以及委任相关的班级指导员。rn3.设置系统参数。rn4.可以对设置该校的级长/校主任/校长。rn5.对所有数据进行备份和还原。rn6.记录管理员登陆信息。rn7.~~~~~I还没有想好,应该是很全的,只是想不起来。rn

没有更多推荐了,返回首页