903. Valid Permutations for DI Sequence
題目網址:https://leetcode.cn/problems/valid-permutations-for-di-sequence/
題意:給一長度為
n
的 strings
, 其中s[i]
可能是:
D
代表減少I
代表增加有效排列是對在
[0, n]
範圍內的所有整數的一個排列perm
, 使得對所有的i
滿足以下規則:
- 若
s[i] == 'D'
, 則perm[i] > perm[i + 1]
- 若
s[i] == 'I'
, 則perm[i] < perm[i + 1]
返回有效排列
perm
的數量, 因為答案可能很大, 所以請返回你的答案對$10^9 + 7$
取餘數。
Solution 1:
想法:利用 DP, 由於
s[i]
決定的只是perm
中的「相對」大小, 故perm[i]
的值僅由s[i - 1]
和perm[i - 1]
決定。1. 定義狀態:
- 首先, 會在
s
前先加上一個#
, 代表什麼元素都沒有的狀態dp[i][j]
:s[1:i]
中, 結尾為perm[i] = j
的有效排列數。其中0 ≤ j ≤ i
➔ 相當於是[0, i]
的有效排列數2. 得到轉移方程:
- 若
s[i] == 'I'
, 因perm[i] = j
, 所以有效排列數為加總前一個數s[i - 1]
在區間[0, j - 1]
的有效排列數
➔dp[i][j] = sum(dp[i - 1][k])
, for0 ≤ k ≤ j - 1
- 若
s[i] == 'D'
, 因perm[i] = j
, 那有效排列數是不是為加總前一個數s[i - 1]
在區間[j + 1, i]
的有效排列數呢?其實不是,s[i - 1]
要改成在區間[j, i]
才對
➔dp[i][j] = sum(dp[i - 1][k])
, forj ≤ k ≤ i
理由如下, 假如我們現在已經有了一個
"DID"
的排列1032
。若我們還想加一個D
, 將其變成"DIDD"
的排列, 應該如何加數字呢?多了一個數字4
, 但顯然直接加4
是不行的
- 可以在末尾加
2
, 但是要先把原排列中≥ 2
的數字都先+1
, 即1032
➔1043
, 然後再加2
, 變成10432
- 也可以在末尾加
1
, 但是要先把原排列中≥ 1
的數字都先+1
, 即1032
➔2043
, 然後再加1
, 變成20431
- 也可以在末尾加
0
, 但是要先把原排列中≥ 0
的數字都先+1
, 即1032
➔2143
, 然後再加0
, 變成21430
- 但是, 無法再末尾加
3
或4
, 因為原排列中的最後一位為2
, 所以只有≤ 2
的數能被加在末尾, 否則無法形成降序(s = "DIDD"
, 最後必須是降序)➔ 故
k
的區間為[j, i]
, 其中j
為原排列的末尾perm[i - 1]
之值3. 初始化:
dp[0][0]
:當範圍為[0, 0]
時, 只有1
種有效排列數
➔dp[0][0] = 1
- 其餘皆初始成
0
題目要求的是所有有效的排列數, 故答案為加總最後一位所有的可能性
class Solution { |
- time:$O(n^2)$ ➔ for loop
- space:$O(n^2)$ ➔
dp
Solution 2:
想法:改進 Solution 1, 由於
dp[i][j]
只會用到上一列的狀態, 故只需儲存上一列的狀態即可
class Solution { |
- time:$O(n^2)$ ➔ for loop
- space:$O(n)$ ➔
dp
,prevRow
本部落格所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 Zako's Blog!
評論