class Solution:
def diStringMatch(self, s: str) -> list[int]:
n = len(s)
low, high = 0, n
result = []
for char in s:
if char == 'I':
result.append(low)
low += 1
else:
result.append(high)
high -= 1
result.append(low) # low == high here
return result
class Solution {
public:
vector<int> diStringMatch(string s) {
int n = s.size();
int low = 0, high = n;
vector<int> result;
for (char c : s) {
if (c == 'I') {
result.push_back(low++);
} else {
result.push_back(high--);
}
}
result.push_back(low); // low == high
return result;
}
};
class Solution {
public int[] diStringMatch(String s) {
int n = s.length();
int low = 0, high = n;
int[] result = new int[n + 1];
for (int i = 0; i < n; i++) {
if (s.charAt(i) == 'I') {
result[i] = low++;
} else {
result[i] = high--;
}
}
result[n] = low; // low == high
return result;
}
}
var diStringMatch = function(s) {
let n = s.length;
let low = 0, high = n;
let result = [];
for (let i = 0; i < n; i++) {
if (s[i] === 'I') {
result.push(low++);
} else {
result.push(high--);
}
}
result.push(low); // low == high
return result;
};
You are given a string s
consisting only of the characters 'I' (for "increase") and 'D' (for "decrease"). Your task is to construct a permutation perm
of the integers from 0 to n
(where n = len(s)
) such that:
s[i] == 'I'
, then perm[i] < perm[i+1]
s[i] == 'D'
, then perm[i] > perm[i+1]
Each integer from 0 to n
must be used exactly once in perm
(no repeats). Only one valid solution is required.
For example, if s = "IDID"
, a valid output could be [0,4,1,3,2]
.
At first glance, this problem seems to ask us to generate all possible permutations and check them one by one, but that would be very inefficient, especially since the number of permutations grows rapidly with n
. Instead, we want to find a way to directly construct a valid permutation by following the pattern described by s
.
Since each character in s
tells us whether the next number should be bigger or smaller, we can think about using the smallest and largest available numbers at each step. If we see an 'I', we assign the current smallest unused number; if we see a 'D', we assign the current largest unused number. This way, we always satisfy the required relationship between consecutive elements, and we use up all numbers exactly once.
This approach avoids brute force and leverages a greedy strategy: always make the locally optimal choice (smallest for 'I', largest for 'D') to ensure the global pattern is achieved.
Let's break down the algorithm step by step:
low = 0
and high = n
, where n
is the length of s
. These pointers represent the smallest and largest available numbers, respectively.
s
: For each character:
low
to the current position in the permutation and increment low
by 1.high
to the current position and decrement high
by 1.n+1
numbers for n
positions). Both low
and high
will be equal, so assign this value to the last position in the permutation.
This method is efficient because it processes each character in s
only once, and it never revisits or reuses numbers.
Let's use the sample input s = "IDID"
. The numbers available are 0, 1, 2, 3, 4.
s[0] = 'I'
→ assign low = 0
. Increment low
to 1.s[1] = 'D'
→ assign high = 4
. Decrement high
to 3.s[2] = 'I'
→ assign low = 1
. Increment low
to 2.s[3] = 'D'
→ assign high = 3
. Decrement high
to 2.low = high = 2
remains. Assign to last position.At each step, the assignment ensures that the 'I' and 'D' requirements are satisfied, and all numbers are used exactly once.
Brute-force approach: Generating all possible permutations and checking each one would require O((n+1)!)
time and space, which is infeasible even for moderate values of n
.
Optimized approach (greedy two-pointer):
O(n)
, since we process each character of s
exactly once.O(n)
, for storing the resulting permutation of length n+1
.The DI String Match problem is elegantly solved using a greedy two-pointer approach. By assigning the smallest available number for 'I' and the largest for 'D', we can construct a valid permutation in linear time. This strategy avoids brute-force permutation generation, ensuring both efficiency and correctness. The key insight is to match the pattern directly with available numbers, making the solution both simple and powerful.