手写一遍前三项就能发现规律,过程中发现由于每一次新增一个长度都是在前面的长度(答案)的基础上得到新的答案,这不就是动态规划常用来解决的问题吗,保存子问题,避免重复求解:对于新增一个长度,我们在最后面可以添加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的情况。
1 | 2 | 3 |
---|---|---|
A | LA | LLA |
L | PA | PLA |
P | AL | LPA |
AP | PPA | |
LL | LAL | |
PL | PAL | |
LP | ALL | |
PP | APL | |
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;
}
}