LeetCode 552. 学生出勤记录 II 动态规划

手写一遍前三项就能发现规律,过程中发现由于每一次新增一个长度都是在前面的长度(答案)的基础上得到新的答案,这不就是动态规划常用来解决的问题吗,保存子问题,避免重复求解:对于新增一个长度,我们在最后面可以添加ALP三种字符,既然不能出现两个A,那合法的串就一定要么有一个A要么没有,那我们就在前面一个长度的基础上,把它们分为含A和不含A的两类。对于不含A的,我们还可以分为以L结尾和以P结尾,也就是我们每次都是在后面添加ALP。对于含一个A的,我们也可以分为以A结尾以L结尾和以P结尾,这样分为五类就能涵盖所有情况。

dp数组含义如下,代表该情况下满足条件的串的个数:
dp[0][0]=1;//contains only a ‘A’ & end with a ‘A’
dp[0][1]=0;//contains only a ‘A’ & end with a ‘L’
dp[0][2]=0;//contains only a ‘A’ & end with a ‘P’
dp[0][3]=1;//without any ‘A’ & end with a ‘L’
dp[0][4]=1;//without any ‘A’ & end with a ‘P’

因而很简单的就能得到状态转移关系:
dp[i][0]:仅含一个A,以A结尾,就是在上一个长度,对于那些不含A的基础上在末尾加一个A的情况=dp[i-1][3]+dp[i-1][4]
dp[i][2]:仅含一个A,以P结尾,既然这次加的是P,又要要求只含有一个A,显然dp[i][2]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2],也就是前一个长度仅含A的所有情况相加
dp[i][4]:不含有A,以P结尾,这次加的是P,但是要求不要A的串有多少个,也就是前面一个长度不含A的情况相加dp[i][4]=dp[i-1][3]+dp[i-1][4]
dp[i][3]:不含A,以L结尾,这次加的是L,但是串不能包含A,并且L不连续三次,那么不包含A,就只能找dp[i-1][4]和dp[i-1][3]了,dp[i-1][4]以p结尾的是肯定可以的,直接加上,但是i-1,3这个以L结尾的呢。如果是一个L好说,加上L就是以两个L结尾,但是两个L再加L就是三个了,就不行了。那我们就想这dpi-1的两个L是怎么来的,不还是从dpi-2以L结尾的加了一个L吗,所以我们直接从dpi-2下手,只要两个L,那就dpi-2以P结尾的加一个L,转移到dpi-1,再加一个L,这样就肯定是两个L,肯定满足。所以dp[i][3]=dp[i-1][4]+dp[i-2][4]
同理dp[i][1]:仅包含一个A,以L结尾的情况也是这样考虑来避免三个L相连。dp[i][1]=dp[i-1][0]+dp[i-1][2]+dp[i-2][2]+dp[i-2][0],别忘了不止P还有A的情况。

123
ALALLA
LPAPLA
PALLPA
APPPA
LLLAL
PLPAL
LPALL
PPAPL
LAP
PAP

代码如下,时间O(n),空间O(n),空间可以再优化成O(1),因为我们可以发现每次都只用到了前面两个长度的情况。

class Solution {
    public int checkRecord(int n) {
        if (n==1)
            return 3;
        long[][] dp=new long[n][5];
        dp[0][0]=1;//contains only a 'A' end with a 'A'
        dp[0][1]=0;//contains only a 'A' end with a 'L'
        dp[0][2]=0;//contains only a 'A' end with a 'P'
        dp[0][3]=1;//without any 'A' & end with a 'L'
        dp[0][4]=1;//without any 'A' & end with a 'P'
        dp[1][0]=2; dp[1][1]=1; dp[1][2]=1; dp[1][3]=2;  dp[1][4]=2;
        for (int i=2;i<n;i++) {
            dp[i][4]=dp[i-1][3]+dp[i-1][4];
            dp[i][3]=dp[i-1][4]+dp[i-2][4];
            dp[i][0]=dp[i-1][3]+dp[i-1][4];
            dp[i][1]=dp[i-1][0]+dp[i-1][2]+dp[i-2][2]+dp[i-2][0];
            dp[i][2]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];
            for (int j=0;j<5;j++)
                dp[i][j]%=1000000007;
        }
        long ans=0;
        for (int i=0;i<5;i++) {
            ans+=dp[n-1][i];
        }
        ans%=1000000007;
        return (int)ans;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值