Before attempting this problem, you should be comfortable with:
Stacks - Understanding LIFO behavior to reverse sequences of elements
Greedy Algorithms - Making locally optimal choices to achieve the lexicographically smallest result
Array Manipulation - In-place reversal of subarrays and two-pointer techniques
1. Using Stack
Intuition
To produce the lexicographically smallest permutation, we want to place smaller numbers as early as possible. The string tells us the relationship between consecutive elements: 'I' means increasing, 'D' means decreasing. When we encounter a sequence of 'D's, we need to reverse the order of those numbers. A stack naturally handles this: push numbers onto the stack during 'D' sequences, then pop them all when we hit an 'I' (or the end), which reverses their order.
Algorithm
Initialize an empty result array and a stack.
Iterate through positions 1 to n:
If the character at position i-1 is 'I', push i onto the stack, then pop all elements from the stack into the result.
If the character is 'D', just push i onto the stack.
After the loop, push n+1 onto the stack and pop all remaining elements into the result.
classSolution{/**
* @param {string} s
* @return {number[]}
*/findPermutation(s){const n = s.length;const res =newArray(n +1);const stack =[];let j =0;for(let i =1; i <= n; i++){if(s[i -1]==='I'){
stack.push(i);while(stack.length >0){
res[j++]= stack.pop();}}else{
stack.push(i);}}
stack.push(n +1);while(stack.length >0){
res[j++]= stack.pop();}return res;}}
publicclassSolution{publicint[]FindPermutation(string s){int[] res =newint[s.Length +1];Stack<int> stack =newStack<int>();int j =0;for(int i =1; i <= s.Length; i++){if(s[i -1]=='I'){
stack.Push(i);while(stack.Count >0){
res[j++]= stack.Pop();}}else{
stack.Push(i);}}
stack.Push(s.Length +1);while(stack.Count >0){
res[j++]= stack.Pop();}return res;}}
funcfindPermutation(s string)[]int{
n :=len(s)
res :=make([]int, n+1)
stack :=[]int{}
j :=0for i :=1; i <= n; i++{if s[i-1]=='I'{
stack =append(stack, i)forlen(stack)>0{
res[j]= stack[len(stack)-1]
stack = stack[:len(stack)-1]
j++}}else{
stack =append(stack, i)}}
stack =append(stack, n+1)forlen(stack)>0{
res[j]= stack[len(stack)-1]
stack = stack[:len(stack)-1]
j++}return res
}
class Solution {funfindPermutation(s: String): IntArray {val n = s.length
val res =IntArray(n +1)val stack = ArrayDeque<Int>()var j =0for(i in1..n){if(s[i -1]=='I'){
stack.addLast(i)while(stack.isNotEmpty()){
res[j++]= stack.removeLast()}}else{
stack.addLast(i)}}
stack.addLast(n +1)while(stack.isNotEmpty()){
res[j++]= stack.removeLast()}return res
}}
classSolution{funcfindPermutation(_ s:String)->[Int]{let n = s.count
let chars =Array(s)var res =[Int](repeating:0, count: n +1)var stack =[Int]()var j =0for i in1...n {if chars[i -1]=="I"{
stack.append(i)while!stack.isEmpty {
res[j]= stack.removeLast()
j +=1}}else{
stack.append(i)}}
stack.append(n +1)while!stack.isEmpty {
res[j]= stack.removeLast()
j +=1}return res
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
Where n is the number of elements in the resultant arrangement
2. Reversing the Subarray
Intuition
Start with the identity permutation [1, 2, ..., n+1], which is already the smallest possible arrangement. Whenever we see a sequence of consecutive 'D's, we need those corresponding elements to be in decreasing order. We can achieve this by reversing the subarray that spans those 'D's. This maintains the smallest possible values in earlier positions while satisfying the decrease constraints.
Algorithm
Initialize the result array as [1, 2, 3, ..., n+1].
Iterate through the string:
When a 'D' is encountered, find the end of the consecutive 'D' sequence.
Reverse the subarray from the position before the first 'D' to the position after the last 'D'.
classSolution:deffindPermutation(self, s:str)-> List[int]:
n =len(s)
res =[i +1for i inrange(n +1)]
i =1while i <= n:
j = i
while i <= n and s[i -1]=='D':
i +=1
self.reverse(res, j -1, i)
i +=1return res
defreverse(self, a: List[int], start:int, end:int)->None:for i inrange((end - start)//2):
temp = a[i + start]
a[i + start]= a[end - i -1]
a[end - i -1]= temp
classSolution{publicint[]findPermutation(String s){int[] res =newint[s.length()+1];for(int i =0; i < res.length; i++){
res[i]= i +1;}int i =1;while(i <= s.length()){int j = i;while(i <= s.length()&& s.charAt(i -1)=='D'){
i++;}reverse(res, j -1, i);
i++;}return res;}publicvoidreverse(int[] a,int start,int end){for(int i =0; i <(end - start)/2; i++){int temp = a[i + start];
a[i + start]= a[end - i -1];
a[end - i -1]= temp;}}}
classSolution{public:
vector<int>findPermutation(string s){int n = s.length();
vector<int>res(n +1);for(int i =0; i < res.size(); i++){
res[i]= i +1;}int i =1;while(i <= n){int j = i;while(i <= n && s[i -1]=='D'){
i++;}reverse(res, j -1, i);
i++;}return res;}voidreverse(vector<int>& a,int start,int end){for(int i =0; i <(end - start)/2; i++){int temp = a[i + start];
a[i + start]= a[end - i -1];
a[end - i -1]= temp;}}};
classSolution{/**
* @param {string} s
* @return {number[]}
*/findPermutation(s){const n = s.length;const res =newArray(n +1);for(let i =0; i < res.length; i++){
res[i]= i +1;}let i =1;while(i <= n){let j = i;while(i <= n && s[i -1]==='D'){
i++;}this.reverse(res, j -1, i);
i++;}return res;}/**
* @param {number[]} a
* @param {number} start
* @param {number} end
*/reverse(a, start, end){for(let i =0; i < Math.floor((end - start)/2); i++){let temp = a[i + start];
a[i + start]= a[end - i -1];
a[end - i -1]= temp;}}}
publicclassSolution{publicint[]FindPermutation(string s){int n = s.Length;int[] res =newint[n +1];for(int idx =0; idx < res.Length; idx++){
res[idx]= idx +1;}int i =1;while(i <= n){int j = i;while(i <= n && s[i -1]=='D'){
i++;}Reverse(res, j -1, i);
i++;}return res;}privatevoidReverse(int[] a,int start,int end){for(int i =0; i <(end - start)/2; i++){int temp = a[i + start];
a[i + start]= a[end - i -1];
a[end - i -1]= temp;}}}
funcfindPermutation(s string)[]int{
n :=len(s)
res :=make([]int, n+1)for i :=range res {
res[i]= i +1}
i :=1for i <= n {
j := i
for i <= n && s[i-1]=='D'{
i++}reverse(res, j-1, i)
i++}return res
}funcreverse(a []int, start, end int){for i :=0; i <(end-start)/2; i++{
a[i+start], a[end-i-1]= a[end-i-1], a[i+start]}}
class Solution {funfindPermutation(s: String): IntArray {val n = s.length
val res =IntArray(n +1){ it +1}var i =1while(i <= n){val j = i
while(i <= n && s[i -1]=='D'){
i++}reverse(res, j -1, i)
i++}return res
}privatefunreverse(a: IntArray, start: Int, end: Int){for(i in0until(end - start)/2){val temp = a[i + start]
a[i + start]= a[end - i -1]
a[end - i -1]= temp
}}}
classSolution{funcfindPermutation(_ s:String)->[Int]{let n = s.count
let chars =Array(s)var res =(1...n +1).map {$0}var i =1while i <= n {let j = i
while i <= n && chars[i -1]=="D"{
i +=1}reverse(&res, j -1, i)
i +=1}return res
}privatefuncreverse(_ a:inout[Int],_ start:Int,_ end:Int){for i in0..<(end - start)/2{let temp = a[i + start]
a[i + start]= a[end - i -1]
a[end - i -1]= temp
}}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
Where n is the size of the resultant array
3. Two Pointers
Intuition
We can build the result directly without explicitly reversing subarrays. For 'I' characters, we simply place the next available number. For 'D' sequences, we need to place a decreasing run of numbers. When we find a sequence of k consecutive 'D's, we fill those k+1 positions with decreasing values starting from the highest value in that range. This is done by identifying the 'D' segment boundaries and filling values in reverse order.
Algorithm
Initialize result[0] = 1 and iterate through the string.
For each position:
If the character is 'I', set the default value for the current position.
If the character is 'D', find the entire consecutive 'D' sequence.
Fill the positions covered by this 'D' sequence with decreasing values.
classSolution:deffindPermutation(self, s:str)-> List[int]:
n =len(s)
res =[0]*(n +1)
res[0]=1
i =1while i <= n:
res[i]= i +1
j = i
if s[i -1]=='D':while i <= n and s[i -1]=='D':
i +=1
k, c = j -1, i
while k <= i -1:
res[k]= c
k +=1
c -=1else:
i +=1return res
classSolution{publicint[]findPermutation(String s){int[] res =newint[s.length()+1];
res[0]=1;int i =1;while(i <= s.length()){
res[i]= i +1;int j = i;if(s.charAt(i -1)=='D'){while(i <= s.length()&& s.charAt(i -1)=='D'){
i++;}for(int k = j -1, c = i; k <= i -1; k++, c--){
res[k]= c;}}else{
i++;}}return res;}}
classSolution{public:
vector<int>findPermutation(string s){int n = s.length();
vector<int>res(n +1);
res[0]=1;int i =1;while(i <= n){
res[i]= i +1;int j = i;if(s[i -1]=='D'){while(i <= n && s[i -1]=='D'){
i++;}for(int k = j -1, c = i; k <= i -1; k++, c--){
res[k]= c;}}else{
i++;}}return res;}};
classSolution{/**
* @param {string} s
* @return {number[]}
*/findPermutation(s){const n = s.length;const res =newArray(n +1);
res[0]=1;let i =1;while(i <= n){
res[i]= i +1;let j = i;if(s[i -1]==='D'){while(i <= n && s[i -1]==='D'){
i++;}for(let k = j -1, c = i; k <= i -1; k++, c--){
res[k]= c;}}else{
i++;}}return res;}}
publicclassSolution{publicint[]FindPermutation(string s){int n = s.Length;int[] res =newint[n +1];
res[0]=1;int i =1;while(i <= n){
res[i]= i +1;int j = i;if(s[i -1]=='D'){while(i <= n && s[i -1]=='D'){
i++;}for(int k = j -1, c = i; k <= i -1; k++, c--){
res[k]= c;}}else{
i++;}}return res;}}
funcfindPermutation(s string)[]int{
n :=len(s)
res :=make([]int, n+1)
res[0]=1
i :=1for i <= n {
res[i]= i +1
j := i
if s[i-1]=='D'{for i <= n && s[i-1]=='D'{
i++}for k, c := j-1, i; k <= i-1; k, c = k+1, c-1{
res[k]= c
}}else{
i++}}return res
}
class Solution {funfindPermutation(s: String): IntArray {val n = s.length
val res =IntArray(n +1)
res[0]=1var i =1while(i <= n){
res[i]= i +1val j = i
if(s[i -1]=='D'){while(i <= n && s[i -1]=='D'){
i++}var k = j -1var c = i
while(k <= i -1){
res[k]= c
k++
c--}}else{
i++}}return res
}}
classSolution{funcfindPermutation(_ s:String)->[Int]{let n = s.count
let chars =Array(s)var res =[Int](repeating:0, count: n +1)
res[0]=1var i =1while i <= n {
res[i]= i +1let j = i
if chars[i -1]=="D"{while i <= n && chars[i -1]=="D"{
i +=1}var k = j -1var c = i
while k <= i -1{
res[k]= c
k +=1
c -=1}}else{
i +=1}}return res
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
Where n is the size of the resultant array
Common Pitfalls
Forgetting to Handle the Final Element
The result array has n+1 elements for a string of length n. After processing all characters in the string, you must still handle the last number (n+1). Whether using a stack or reversing approach, forgetting to process this final element results in an incomplete permutation with missing or zero values.
Misunderstanding the Stack's Role
When using a stack approach, the stack reverses the order of consecutive numbers during 'D' sequences. A common mistake is popping immediately on every character instead of accumulating during 'D' runs and only popping when encountering 'I'. This leads to incorrect orderings that do not satisfy the decrease constraints.
Off-by-One Index Errors
The string uses 0-based indexing while the permutation uses values 1 to n+1. Converting between string indices and permutation values requires careful attention. For example, s[i-1] corresponds to the relationship between positions i and i+1 in the result. Confusing these mappings produces permutations that do not match the input pattern.