Given a grid with row
rows and column
columns, you start at the top-left cell (0, 0) and want to reach the bottom-right cell (row-1
, column-1
). You may only move down or right at each step. A move down is represented by the letter 'V'
(for vertical), and a move right by 'H'
(for horizontal).
You're given an integer k
. Your task is to return the k-th lexicographically smallest sequence of instructions that will take you from the top-left to the bottom-right corner of the grid. Each instruction sequence is a string of length row + column - 2
consisting of exactly row - 1
'V's and column - 1
'H's.
Constraints:
row
, column
≤ 15k
≤ C(row + column - 2, row - 1)The brute-force way to solve this problem would be to generate all possible instruction sequences (permutations of 'H' and 'V'), sort them, and pick the k-th smallest one. However, this is inefficient because the number of possible sequences grows rapidly with the grid size (it's the binomial coefficient C(row + column - 2, row - 1)).
Instead, we can think recursively. At each step, we can choose to move either right ('H') or down ('V'), as long as we do not exceed the allowed number of each move. The key insight is that the number of possible sequences with a certain number of 'H's and 'V's remaining can be calculated using combinations. This allows us to "skip" large blocks of sequences and directly construct the k-th sequence letter by letter, without generating all possible paths.
We'll construct the k-th smallest instruction sequence step by step, always choosing between 'H' and 'V' based on how many sequences start with each possible move. Here's how:
h
'H's and v
'V's left, the number of sequences starting with 'H' is C(h + v - 1, v).
h
by one.
k
, and decrease v
by one.
We use a helper function to compute combinations efficiently. This approach avoids generating all sequences and directly constructs the answer in O(row + column) steps.
Input: row = 2
, column = 3
, k = 3
Total moves needed: 2 'H's and 1 'V' (since row - 1 = 1, column - 1 = 2).
All possible sequences (sorted):
Step-by-step:
The combination calculation is efficient since the numbers involved are small (maximum 28 choose 14 for a 15x15 grid).
This problem can be solved elegantly by leveraging combinatorics to count the number of possible sequences at each decision point. Instead of generating all possible paths, we use the binomial coefficient to decide whether to place an 'H' or 'V' at each step, efficiently constructing the k-th lexicographically smallest instruction. This approach is both fast and memory-efficient, and it demonstrates the power of mathematical reasoning in algorithm design.
import math
class Solution:
def kthSmallestPath(self, destination, k):
row, col = destination
v = row
h = col
res = []
for _ in range(row + col):
if h == 0:
res.append('V')
v -= 1
elif v == 0:
res.append('H')
h -= 1
else:
comb = math.comb(h + v - 1, v)
if k <= comb:
res.append('H')
h -= 1
else:
res.append('V')
k -= comb
v -= 1
return ''.join(res)
#include <vector>
#include <string>
using namespace std;
class Solution {
public:
long long comb(int n, int k) {
long long res = 1;
for (int i = 1; i <= k; ++i) {
res = res * (n - i + 1) / i;
}
return res;
}
string kthSmallestPath(vector<int>& destination, int k) {
int v = destination[0], h = destination[1];
string res = "";
for (int i = 0; i < v + h; ++i) {
if (h == 0) {
res += 'V';
v--;
} else if (v == 0) {
res += 'H';
h--;
} else {
long long c = comb(h + v - 1, v);
if (k <= c) {
res += 'H';
h--;
} else {
res += 'V';
k -= c;
v--;
}
}
}
return res;
}
};
class Solution {
public String kthSmallestPath(int[] destination, int k) {
int v = destination[0], h = destination[1];
StringBuilder res = new StringBuilder();
for (int i = 0; i < v + h; ++i) {
if (h == 0) {
res.append('V');
v--;
} else if (v == 0) {
res.append('H');
h--;
} else {
long c = comb(h + v - 1, v);
if (k <= c) {
res.append('H');
h--;
} else {
res.append('V');
k -= c;
v--;
}
}
}
return res.toString();
}
private long comb(int n, int k) {
long res = 1;
for (int i = 1; i <= k; ++i) {
res = res * (n - i + 1) / i;
}
return res;
}
}
var kthSmallestPath = function(destination, k) {
let v = destination[0], h = destination[1];
let res = [];
function comb(n, k) {
let res = 1;
for (let i = 1; i <= k; ++i) {
res = res * (n - i + 1) / i;
}
return res;
}
for (let i = 0; i < v + h; ++i) {
if (h === 0) {
res.push('V');
v--;
} else if (v === 0) {
res.push('H');
h--;
} else {
let c = comb(h + v - 1, v);
if (k <= c) {
res.push('H');
h--;
} else {
res.push('V');
k -= c;
v--;
}
}
}
return res.join('');
};