此题和LC940.Distinct-Subsequences-II
的联系非常紧密。我们可以先用LC940的方法计算所有distinct subsequence的总数。令dp[i]表示前i个字符的前缀里有多少个不同的子序列(包括有先导零的子序列以及空子序列)。核心代码如下:
for (int i=1; i<=n; i++)
{
int j = s[i]=='0' ? last0[i] : last1[i];
dp[i] = dp[i-1]*2 - dp[j-1];
}
那么接下来如何转变为题目要求的“无先导零”的子序列呢?我们发现上述式子里的dp[i]都依赖于dp[i-1],所以我们只要控制初始的dp值即可。假设binary的开头有m个零,那么显然dp[1]到dp[m]都应该是0,因为它们无法拼凑出任何合法的子序列。而dp[m+1]应该是1,因为它只能构造“1”这一个合法的子序列。此后对于i>m+1,利用上述的代码能够得到所有正确的dp[i]。
这里给个直观的解释。首先,从i=m+1开始+ dp[i-1]*2
这部分,意思是在前面已有的合法子序列的基础上,append s[i] or not,因此所对应的一定都是已经以1开头的子序列。其次-dp[j-1]
这部分,去重的是形如x x x s[i]
这样的字符串,其中x x x
是前j-1个字符所能构建的distinct subsequence. 如果j-1<=m,那么这些dp[j-1]都预置为0,所以去重的部分也不会包括以0开头的子序列。综上所述,每一步所计算的dp[i]都表达的是以1开头的子序列。
此外需要说明的是,很多题解的代码写成了如下的形式:
if (s[i]=='0')
dp[i][0] = dp[i-1][0]+dp[i-1][1];
else
dp[i][1] = dp[i-1][0]+dp[i-1][1]+1;
这样的结果是对的,但简洁的代码背后,理解上其实有很大的思维的跳跃,我觉得不够清晰。上面的表达式dp[i][0],看上去是说前i个字符构成的、以0结尾的合法子序列,必须是在前i-1个字符构成的合法子序列的基础上append当前s[i]所对应的这个0。dp[i][1]的解释同理类似,但多了一个单独以s[i]构造“1”的情况。但这个其实是没有道理的,我们完全有权利不使用s[i]这个字符呀。所以还有更深的微妙的分析隐藏在里面,对于这个代码的解释并不像表面上那么容易。国服的官方解答就是用了很大的篇幅才对上面的代码做了比较全面的解释。