Number of Ways to Rearrange Sticks With K Sticks Visible
Problem
There are n uniquely-sized sticks whose lengths are integers from 1 to n. You want to arrange the sticks such that exactly k sticks are visible from the left. A stick is visible from the left if there are no longer sticks to the left of it.
- For example, if the sticks are arranged
[1,3,2,5,4], then the sticks with lengths1,3, and5are visible from the left.
Given n and k, return the number of such arrangements. Since the answer may be large, return it modulo 10^9 + 7.
Examples
Example 1:
Input:
n = 3, k = 2
Output:
3
Explanation: [1,3,2], [2,3,1], and [2,1,3] are the only arrangements such that exactly 2 sticks are visible.
The visible sticks are underlined.
Example 2:
Input:
n = 5, k = 5
Output:
1
Explanation: [1,2,3,4,5] is the only arrangement such that all 5 sticks are visible.
The visible sticks are underlined.
Example 3:
Input:
n = 20, k = 11
Output:
647427950
Explanation: There are 647427950 (mod 10^9 + 7) ways to rearrange the sticks such that exactly 11 sticks are visible.
Solution
Method 1 - Recursion
We need to arrange the sticks so that exactly k sticks are visible from the left. A stick is visible only if all sticks to its left are shorter.
Key observations:
- If we have
nsticks and want exactlykvisible:- Placing any stick smaller than
nat the last position means it will be hidden, so we still needkvisible sticks among the remainingn-1sticks. - Placing the largest stick (
n) at the last position guarantees it is visible, so we needk-1visible sticks among the remainingn-1sticks.
- Placing any stick smaller than
Base cases:
- If
n == k, only one way (sorted order). - If
k == 0orn == 0, ways = 0. - If
k > n, ways = 0 (impossible).
Code
C++
class Solution {
public:
int rearrangeSticks(int n, int k) {
int mod = 1e9 + 7;
return dfs(n, k, mod);
}
int dfs(int n, int k, int mod) {
if (n == k) return 1;
if (k == 0 || n == 0 || k > n) return 0;
long res = (long)(n - 1) * dfs(n - 1, k, mod) % mod;
res = (res + dfs(n - 1, k - 1, mod)) % mod;
return (int)res;
}
};
Go
func rearrangeSticks(n int, k int) int {
mod := int(1e9 + 7)
var dfs func(int, int) int
dfs = func(n, k int) int {
if n == k {
return 1
}
if k == 0 || n == 0 || k > n {
return 0
}
res := (n-1)*dfs(n-1, k)%mod
res = (res + dfs(n-1, k-1))%mod
return res
}
return dfs(n, k)
}
Java
class Solution {
public int rearrangeSticks(int n, int k) {
int mod = (int)1e9 + 7;
return dfs(n, k, mod);
}
private int dfs(int n, int k, int mod) {
if (n == k) return 1;
if (k == 0 || n == 0 || k > n) return 0;
long res = (long)(n - 1) * dfs(n - 1, k, mod) % mod;
res = (res + dfs(n - 1, k - 1, mod)) % mod;
return (int)res;
}
}
Kotlin
class Solution {
fun rearrangeSticks(n: Int, k: Int): Int {
val mod = 1_000_000_007
fun dfs(n: Int, k: Int): Int {
if (n == k) return 1
if (k == 0 || n == 0 || k > n) return 0
var res = ((n - 1).toLong() * dfs(n - 1, k) % mod)
res = (res + dfs(n - 1, k - 1)) % mod
return res.toInt()
}
return dfs(n, k)
}
}
Python
class Solution:
def rearrangeSticks(self, n: int, k: int) -> int:
mod = 10**9 + 7
def dfs(n: int, k: int) -> int:
if n == k:
return 1
if k == 0 or n == 0 or k > n:
return 0
res = (n - 1) * dfs(n - 1, k) % mod
res = (res + dfs(n - 1, k - 1)) % mod
return res
return dfs(n, k)
Rust
impl Solution {
pub fn rearrange_sticks(n: i32, k: i32) -> i32 {
fn dfs(n: i32, k: i32, modv: i32) -> i32 {
if n == k { return 1; }
if k == 0 || n == 0 || k > n { return 0; }
let mut res = ((n - 1) as i64 * dfs(n - 1, k, modv) as i64) % (modv as i64);
res = (res + dfs(n - 1, k - 1, modv) as i64) % (modv as i64);
res as i32
}
dfs(n, k, 1_000_000_007)
}
}
Typescript
class Solution {
rearrangeSticks(n: number, k: number): number {
const mod = 1e9 + 7;
function dfs(n: number, k: number): number {
if (n === k) return 1;
if (k === 0 || n === 0 || k > n) return 0;
let res = ((n - 1) * dfs(n - 1, k)) % mod;
res = (res + dfs(n - 1, k - 1)) % mod;
return res;
}
return dfs(n, k);
}
}
Complexity
- ⏰ Time complexity:
O(2^n), since each call can branch into two subproblems and there are no memoized results. - 🧺 Space complexity:
O(n), due to recursion stack.
Method 2 - Top-Down DP + Memoization
Code
C++
class Solution {
public:
int rearrangeSticks(int n, int k) {
int mod = 1e9 + 7;
vector<vector<int>> memo(n + 1, vector<int>(k + 1, -1));
return dfs(n, k, mod, memo);
}
int dfs(int n, int k, int mod, vector<vector<int>>& memo) {
if (n == k) return 1;
if (k == 0 || n == 0 || k > n) return 0;
if (memo[n][k] != -1) return memo[n][k];
long res = (long)(n - 1) * dfs(n - 1, k, mod, memo) % mod;
res = (res + dfs(n - 1, k - 1, mod, memo)) % mod;
memo[n][k] = (int)res;
return memo[n][k];
}
};
Go
func rearrangeSticks(n int, k int) int {
mod := int(1e9 + 7)
memo := make(map[[2]int]int)
var dfs func(int, int) int
dfs = func(n, k int) int {
if n == k {
return 1
}
if k == 0 || n == 0 || k > n {
return 0
}
key := [2]int{n, k}
if v, ok := memo[key]; ok {
return v
}
res := (n-1)*dfs(n-1, k)%mod
res = (res + dfs(n-1, k-1))%mod
memo[key] = res
return res
}
return dfs(n, k)
}
Java
class Solution {
public int rearrangeSticks(int n, int k) {
int mod = (int)1e9 + 7;
Integer[][] memo = new Integer[n + 1][k + 1];
return dfs(n, k, mod, memo);
}
private int dfs(int n, int k, int mod, Integer[][] memo) {
if (n == k) return 1;
if (k == 0 || n == 0 || k > n) return 0;
if (memo[n][k] != null) return memo[n][k];
long res = (long)(n - 1) * dfs(n - 1, k, mod, memo) % mod;
res = (res + dfs(n - 1, k - 1, mod, memo)) % mod;
memo[n][k] = (int)res;
return memo[n][k];
}
}
Kotlin
class Solution {
fun rearrangeSticks(n: Int, k: Int): Int {
val mod = 1_000_000_007
val memo = Array(n + 1) { Array(k + 1) { -1 } }
fun dfs(n: Int, k: Int): Int {
if (n == k) return 1
if (k == 0 || n == 0 || k > n) return 0
if (memo[n][k] != -1) return memo[n][k]
var res = ((n - 1).toLong() * dfs(n - 1, k) % mod)
res = (res + dfs(n - 1, k - 1)) % mod
memo[n][k] = res.toInt()
return memo[n][k]
}
return dfs(n, k)
}
}
Python
class Solution:
def rearrangeSticks(self, n: int, k: int) -> int:
mod = 10**9 + 7
memo = {}
def dfs(n: int, k: int) -> int:
if n == k:
return 1
if k == 0 or n == 0 or k > n:
return 0
if (n, k) in memo:
return memo[(n, k)]
res = (n - 1) * dfs(n - 1, k) % mod
res = (res + dfs(n - 1, k - 1)) % mod
memo[(n, k)] = res
return res
return dfs(n, k)
Rust
impl Solution {
pub fn rearrange_sticks(n: i32, k: i32) -> i32 {
fn dfs(n: i32, k: i32, modv: i32, memo: &mut Vec<Vec<i32>>) -> i32 {
if n == k { return 1; }
if k == 0 || n == 0 || k > n { return 0; }
if memo[n as usize][k as usize] != -1 { return memo[n as usize][k as usize]; }
let mut res = ((n - 1) as i64 * dfs(n - 1, k, modv, memo) as i64) % (modv as i64);
res = (res + dfs(n - 1, k - 1, modv, memo) as i64) % (modv as i64);
memo[n as usize][k as usize] = res as i32;
res as i32
}
let mut memo = vec![vec![-1; (k + 1) as usize]; (n + 1) as usize];
dfs(n, k, 1_000_000_007, &mut memo)
}
}
Typescript
class Solution {
rearrangeSticks(n: number, k: number): number {
const mod = 1e9 + 7;
const memo: Record<string, number> = {};
function dfs(n: number, k: number): number {
if (n === k) return 1;
if (k === 0 || n === 0 || k > n) return 0;
const key = `${n},${k}`;
if (memo[key] !== undefined) return memo[key];
let res = ((n - 1) * dfs(n - 1, k)) % mod;
res = (res + dfs(n - 1, k - 1)) % mod;
memo[key] = res;
return res;
}
return dfs(n, k);
}
}
Complexity
- ⏰ Time complexity:
O(nk), since each subproblem (n,k) is solved only once and stored in the memoization table. - 🧺 Space complexity:
O(nk), for the memoization table and recursion stack.
Method 3 - Bottom-Up DP (Tabulation)
Intuition
We use a DP table to build the solution iteratively. For each number of sticks and visible sticks, we compute the number of arrangements using previously computed results, avoiding recursion and memoization.
Approach
- Create a DP table
dp[n+1][k+1]wheredp[i][j]is the number of ways to arrangeisticks withjvisible. - Initialize
dp[0][0] = 1(base case). - For each
ifrom 1 ton, and eachjfrom 1 tomin(i, k), compute:dp[i][j] = (dp[i-1][j] * (i-1) + dp[i-1][j-1]) % mod- First term: place a smaller stick at the end (hidden), second term: place the largest stick at the end (visible).
- Return
dp[n][k].
Code
C++
class Solution {
public:
int rearrangeSticks(int n, int k) {
int mod = 1e9 + 7;
vector<vector<long>> dp(n + 1, vector<long>(k + 1, 0));
dp[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= min(i, k); ++j) {
dp[i][j] = (dp[i-1][j] * (i-1) + dp[i-1][j-1]) % mod;
}
}
return (int)dp[n][k];
}
};
Go
func rearrangeSticks(n int, k int) int {
mod := int(1e9 + 7)
dp := make([][]int, n+1)
for i := range dp {
dp[i] = make([]int, k+1)
}
dp[0][0] = 1
for i := 1; i <= n; i++ {
for j := 1; j <= k && j <= i; j++ {
dp[i][j] = (dp[i-1][j]*(i-1) + dp[i-1][j-1]) % mod
}
}
return dp[n][k]
}
Java
class Solution {
public int rearrangeSticks(int n, int k) {
int mod = (int)1e9 + 7;
long[][] dp = new long[n+1][k+1];
dp[0][0] = 1L;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= Math.min(i, k); j++) {
dp[i][j] = (dp[i-1][j] * (i-1) + dp[i-1][j-1]) % mod;
}
}
return (int)dp[n][k];
}
}
Kotlin
class Solution {
fun rearrangeSticks(n: Int, k: Int): Int {
val mod = 1_000_000_007
val dp = Array(n + 1) { LongArray(k + 1) }
dp[0][0] = 1L
for (i in 1..n) {
for (j in 1..minOf(i, k)) {
dp[i][j] = (dp[i-1][j] * (i-1) + dp[i-1][j-1]) % mod
}
}
return dp[n][k].toInt()
}
}
Python
class Solution:
def rearrangeSticks(self, n: int, k: int) -> int:
mod = 10**9 + 7
dp = [[0] * (k + 1) for _ in range(n + 1)]
dp[0][0] = 1
for i in range(1, n + 1):
for j in range(1, min(i, k) + 1):
dp[i][j] = (dp[i-1][j] * (i-1) + dp[i-1][j-1]) % mod
return dp[n][k]
Rust
impl Solution {
pub fn rearrange_sticks(n: i32, k: i32) -> i32 {
let modv = 1_000_000_007;
let n = n as usize;
let k = k as usize;
let mut dp = vec![vec![0i64; k + 1]; n + 1];
dp[0][0] = 1;
for i in 1..=n {
for j in 1..=k.min(i) {
dp[i][j] = (dp[i-1][j] * (i as i64 - 1) + dp[i-1][j-1]) % modv;
}
}
dp[n][k] as i32
}
}
Typescript
class Solution {
rearrangeSticks(n: number, k: number): number {
const mod = 1e9 + 7;
const dp: number[][] = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0));
dp[0][0] = 1;
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= Math.min(i, k); j++) {
dp[i][j] = (dp[i-1][j] * (i-1) + dp[i-1][j-1]) % mod;
}
}
return dp[n][k];
}
}
Complexity
- ⏰ Time complexity:
O(nk), since we fill a DP table of sizen x kand each entry takes constant time. - 🧺 Space complexity:
O(nk), for the DP table.