一、题目描述
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'
, thenP[i] > P[i+1]
, and; - If
S[i] == 'I'
, thenP[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 <= S.length <= 200
S
consists only of characters from the set{'D', 'I'}
.
二、题目分析
根据题意,我们需要对从0开始的连续整数 { 0, 1, ..., n }
进行排列,使之符合输入的 DI 字符串(长度为 n
),输出总的符合要求的排列数,这里先把关于长度为 n
的 DI 字符串的问题记作 nDI
。
这道题我们可以用动态规划的思想,从分解原问题着手,来看看 (i-1)DI
和 iDI
的关系。下面使用 res[i][j] ( j <= i )
表示 { 0, 1, ..., i }
中长度为 i+1
,并以数字 j
作为最后一个数字的排列的数量。
-
当下一个即第
i
个字符为D
时,首先显然,某个数字j
只能排在j+1
、j+2
…i-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
。
- e.g. 排列
-
当下一个即第
i
个字符为I
时,与D
的情况相反,某个数字j
只能排在j-1
、j-2
…0
之后,故我们有res[i][j] = res[i-1][j-1] + res[i-1][j-2] + ... + res[i-1][0]
。同样,当数字重复出现时将大于等于j
的数字加一再在末尾放上数字j
。当然,若j = 0
,res[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
。
- e.g. 排列
最后再说明一下,当下一个字符为 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;
}
};