Problem

Given a string s with distinct or duplicate characters, print or return all permutations of s in lexicographic (sorted) order.

Examples

Example 1

1
2
Input: s = "abc"
Output: ["abc","acb","bac","bca","cab","cba"]

Solution

Method 1 — Next-permutation (iterative)

Intuition

The next_permutation algorithm transforms the current permutation into the next larger permutation in lexicographic order. Starting from the characters sorted in non-decreasing order, repeatedly applying next_permutation enumerates all permutations in increasing lexicographic order until no next permutation exists.

Approach

  • Sort s (so the first permutation is the smallest lexicographically).
  • Print or collect the current permutation.
  • While next_permutation succeeds, move to and collect the next permutation.

This is simple, efficient, and uses O(1) additional space (beyond output and the string) if a language provides next_permutation (C++). For languages without a standard helper, implement the next-permutation algorithm: find pivot, swap with successor, reverse suffix.

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution {
public:
  vector<string> permute(string s) {
    vector<string> ans;
    sort(s.begin(), s.end());
    do {
      ans.push_back(s);
    } while (next_permutation(s.begin(), s.end()));
    return ans;
  }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import "sort"

type Solution struct{}

func (Solution) Permute(s string) []string {
  arr := []rune(s)
  sort.Slice(arr, func(i, j int) bool { return arr[i] < arr[j] })
  var ans []string
  ans = append(ans, string(arr))
  for nextPermutationRunes(arr) {
    ans = append(ans, string(arr))
  }
  return ans
}

func nextPermutationRunes(a []rune) bool {
  n := len(a)
  i := n - 2
  for i >= 0 && a[i] >= a[i+1] { i-- }
  if i < 0 { return false }
  j := n - 1
  for a[j] <= a[i] { j-- }
  a[i], a[j] = a[j], a[i]
  for l, r := i+1, n-1; l < r; l, r = l+1, r-1 { a[l], a[r] = a[r], a[l] }
  return true
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.*;

class Solution {
  public List<String> permute(String s) {
    List<String> ans = new ArrayList<>();
    char[] arr = s.toCharArray();
    Arrays.sort(arr);
    ans.add(new String(arr));
    while (nextPermutation(arr)) {
      ans.add(new String(arr));
    }
    return ans;
  }

  private boolean nextPermutation(char[] a) {
    int n = a.length;
    int i = n - 2;
    while (i >= 0 && a[i] >= a[i+1]) i--;
    if (i < 0) return false;
    int j = n - 1;
    while (a[j] <= a[i]) j--;
    char tmp = a[i]; a[i] = a[j]; a[j] = tmp;
    reverse(a, i+1, n-1);
    return true;
  }

  private void reverse(char[] a, int l, int r) {
    while (l < r) { char t = a[l]; a[l++] = a[r]; a[r--] = t; }
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
  fun permute(s: String): List<String> {
    val arr = s.toCharArray().apply { sort() }
    val ans = mutableListOf<String>()
    ans.add(String(arr))
    while (nextPermutation(arr)) ans.add(String(arr))
    return ans
  }

  private fun nextPermutation(a: CharArray): Boolean {
    var i = a.size - 2
    while (i >= 0 && a[i] >= a[i+1]) i--
    if (i < 0) return false
    var j = a.size - 1
    while (a[j] <= a[i]) j--
    val t = a[i]; a[i] = a[j]; a[j] = t
    a.reverse(i+1, a.size)
    return true
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution:
  def permute(self, s: str) -> list[str]:
    arr = sorted(list(s))
    ans: list[str] = []
    ans.append(''.join(arr))
    while self._next_permutation(arr):
      ans.append(''.join(arr))
    return ans

  def _next_permutation(self, a: list[str]) -> bool:
    i = len(a) - 2
    while i >= 0 and a[i] >= a[i+1]:
      i -= 1
    if i < 0:
      return False
    j = len(a) - 1
    while a[j] <= a[i]:
      j -= 1
    a[i], a[j] = a[j], a[i]
    a[i+1:] = reversed(a[i+1:])
    return True
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
pub struct Solution;
impl Solution {
  pub fn permute(s: String) -> Vec<String> {
    let mut arr: Vec<char> = s.chars().collect();
    arr.sort();
    let mut ans = Vec::new();
    ans.push(arr.iter().collect());
    while Self::next_permutation(&mut arr) {
      ans.push(arr.iter().collect());
    }
    ans
  }

  fn next_permutation(a: &mut Vec<char>) -> bool {
    let n = a.len();
    if n < 2 { return false }
    let mut i = n - 2;
    while i > 0 && a[i] >= a[i+1] { i -= 1; }
    if i == 0 && a[0] >= a[1] { return false }
    let mut j = n - 1;
    while a[j] <= a[i] { j -= 1; }
    a.swap(i, j);
    a[i+1..].reverse();
    true
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
export class Solution {
  permute(s: string): string[] {
    const arr = s.split('').sort();
    const n = arr.length;
    const ans: string[] = [];
    ans.push(arr.join(''));
    while (this.nextPermutation(arr)) ans.push(arr.join(''));
    return ans;
  }

  private nextPermutation(a: string[]): boolean {
    let i = a.length - 2;
    while (i >= 0 && a[i] >= a[i+1]) i--;
    if (i < 0) return false;
    let j = a.length - 1;
    while (a[j] <= a[i]) j--;
    [a[i], a[j]] = [a[j], a[i]];
    let l = i+1, r = a.length-1;
    while (l < r) { [a[l], a[r]] = [a[r], a[l]]; l++; r--; }
    return true;
  }
}

Complexity

  • ⏰ Time complexity: O(n * P) where P is the number of permutations (worst-case P = n!). Each transition to the next permutation takes O(n) in the worst case (for the suffix reversal).
  • 🧺 Space complexity: O(n + P*n) including output; O(n) auxiliary for in-place operations.

Method 2 — Sort + deterministic backtracking (lexicographic DFS)

Intuition

If the input is sorted and the backtracking chooses characters in increasing index order while skipping used indices, the generated permutations follow lexicographic order. This is the sorted-skip backtracking pattern: sort first, then backtrack with a used array and the duplicate-skip condition.

Approach

  • Sort s into arr.
  • Use used[] to mark chosen positions.
  • At each level iterate i from 0 to n-1, skipping used indices and skipping i when i>0 && arr[i]==arr[i-1] && !used[i-1].
  • Append when cur.length == n.

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution2 {
public:
  vector<string> permute(string s) {
    int n = s.size();
    vector<string> ans;
    if (n == 0) return ans;
    sort(s.begin(), s.end());
    string cur; cur.reserve(n);
    vector<bool> used(n);
    dfs(s, used, cur, ans);
    return ans;
  }

private:
  void dfs(const string &s, vector<bool> &used, string &cur, vector<string> &ans) {
    int n = s.size();
    if ((int)cur.size() == n) { ans.push_back(cur); return; }
    for (int i = 0; i < n; ++i) {
      if (used[i]) continue;
      if (i > 0 && s[i] == s[i-1] && !used[i-1]) continue;
      used[i] = true;
      cur.push_back(s[i]);
      dfs(s, used, cur, ans);
      cur.pop_back();
      used[i] = false;
    }
  }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.*;

class Solution2 {
  public List<String> permute(String s) {
    List<String> ans = new ArrayList<>();
    if (s == null || s.length() == 0) return ans;
    char[] arr = s.toCharArray();
    Arrays.sort(arr);
    boolean[] used = new boolean[arr.length];
    StringBuilder cur = new StringBuilder();
    dfs(arr, used, cur, ans);
    return ans;
  }

  private void dfs(char[] arr, boolean[] used, StringBuilder cur, List<String> ans) {
    if (cur.length() == arr.length) {
      ans.add(cur.toString());
      return;
    }
    for (int i = 0; i < arr.length; i++) {
      if (used[i]) continue;
      if (i > 0 && arr[i] == arr[i-1] && !used[i-1]) continue;
      used[i] = true;
      cur.append(arr[i]);
      dfs(arr, used, cur, ans);
      cur.deleteCharAt(cur.length() - 1);
      used[i] = false;
    }
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution2:
  def permute(self, s: str) -> list[str]:
    n = len(s)
    if n == 0: return []
    arr = sorted(s)
    used = [False] * n
    ans: list[str] = []

    def dfs(cur: list[str]) -> None:
      if len(cur) == n:
        ans.append(''.join(cur))
        return
      for i in range(n):
        if used[i]:
          continue
        if i > 0 and arr[i] == arr[i-1] and not used[i-1]:
          continue
        used[i] = True
        cur.append(arr[i])
        dfs(cur)
        cur.pop()
        used[i] = False

    dfs([])
    return ans

Complexity

  • ⏰ Time complexity: O(n * P) where P is number of permutations produced; backtracking visits each permutation once and spends O(n) work to build it.
  • 🧺 Space complexity: O(n + P*n) including output; O(n) auxiliary.