LeetCode 903. Valid Permutations for DI Sequence

一、题目描述

We are given S, a length n string of characters from the set {'D', 'I'}. (These letters stand for “decreasing” and “increasing”.)

A valid permutation is a permutation P[0], P[1], ..., P[n] of integers {0, 1, ..., n}, such that for all i:

  • If S[i] == 'D', then P[i] > P[i+1], and;
  • If S[i] == 'I', then P[i] < P[i+1].

How many valid permutations are there? Since the answer may be large, return your answer modulo 10^9 + 7.

Example 1:

Input: “DID”
Output: 5
**Explanation: **
The 5 valid permutations of (0, 1, 2, 3) are:
(1, 0, 3, 2)
(2, 0, 3, 1)
(2, 1, 3, 0)
(3, 0, 2, 1)
(3, 1, 2, 0)

Note:

  1. 1 <= S.length <= 200
  2. S consists only of characters from the set {'D', 'I'}.

二、题目分析

  根据题意,我们需要对从0开始的连续整数 { 0, 1, ..., n } 进行排列,使之符合输入的 DI 字符串(长度为 n ),输出总的符合要求的排列数,这里先把关于长度为 n 的 DI 字符串的问题记作 nDI

  这道题我们可以用动态规划的思想,从分解原问题着手,来看看 (i-1)DIiDI 的关系。下面使用 res[i][j] ( j <= i ) 表示 { 0, 1, ..., i } 中长度为 i+1 ,并以数字 j 作为最后一个数字的排列的数量。

 

  • 当下一个即第 i 个字符为 D 时,首先显然,某个数字 j 只能排在 j+1j+2i-1 之后,故我们有 res[i][j] = res[i-1][j+1] + res[i-1][j+2] + ... + res[i-1][i-1] 。当然这可能会出现数字重复的问题,但由于增加了新的数字 i ,所以在原来的排列的基础上我们能够对数字做出一些调整,即将大于等于 j 的数字全部加一,就可以在末尾再放上数字 j 。如此一来, res[i-1][j] 所代表的排列也能够算到这里,所以最后我们有

      res[i][j] = res[i-1][j] + res[i-1][j+1] + res[i-1][j+2] + ... + res[i-1][i-1]
    
    • e.g. 排列 1, 4, 2, 0, 3 ,下一个字符为 D ,当要在末尾加上 3 的时候,我们将原排列中大于等于 3 的数加一,再加上 3 ,可得排列 1, 5, 2, 0, 4, 3
    • e.g. 排列 0, 3, 2, 1, 4 ,下一个字符为 D ,当要在末尾加上 0 的时候,我们将原排列中大于等于 0 的数加一,再加上 0 ,可得排列 1, 4, 3, 2, 5, 0

 

  • 当下一个即第 i 个字符为 I 时,与 D 的情况相反,某个数字 j 只能排在 j-1j+20 之后,故我们有 res[i][j] = res[i-1][j-1] + res[i-1][j-2] + ... + res[i-1][0] 。同样,当数字重复出现时将大于等于 j 的数字加一再在末尾放上数字 j 。当然,若 j = 0res[i][j] = 0 。所以排除0的情况我们有

      res[i][j] = res[i-1][j-1] + res[i-1][j-2] + ... + res[i-1][0]
    
    • e.g. 排列 1, 4, 2, 0, 3 ,下一个字符为 I ,当要在末尾加上 4 的时候,我们将原排列中大于等于 4 的数加一,再加上 4 ,可得排列 1, 5, 2, 0, 3, 4

最后再说明一下,当下一个字符为 I 时,是无法在末尾加上与上一个排列末尾相同的数字的。即 res[i-1][j] 在下一个字符为 I 的情况下是不能算到 res[i][j] 中的 。


三、具体实现

  经过上面的讨论,我们能看出此时算法的复杂度为 O ( n 3 ) O(n^3) O(n3) ,为了进一步优化,我们将 res[i][j] 改为表示以 j 之前的所有的数结尾的排列数与以 j 结尾的排列的数量的和,这样算法复杂度就达到了 O ( n 2 ) O(n^2) O(n2) 。实现如下:

class Solution
{
  public:
    int numPermsDISequence( string S )
    {
        int len = S.size();
        int mod = 1e9 + 7;

        int **res = new int*[len+1];
        for ( int i = 0; i < len+1; ++i ) {
            res[i] = new int[len+1]();
            res[0][i] = 1;
        }

        for ( int i = 1; i < len + 1; ++i ) {
            for ( int j = 0; j <= i; ++j ) {

                if ( S.at( i - 1 ) == 'D' ) {

                    res[i][j] = ( j == 0 ? 0 : res[i][j-1]  - res[i-1][j-1] ) + res[i-1][i-1];

                } else {

                    res[i][j] = j == 0 ? 0 : res[i][j-1] + res[i-1][j-1];

                }

                if ( res[i][j] < 0 )
                    res[i][j] += mod;
                
                res[i][j] %= mod;

            }
        }

        for ( int i = 0; i < len+1; ++i )
            delete [] res[i];
        delete [] res;

        return res[len][len] % mod;
    }
};

你可能感兴趣的:(LeetCode)