String · 11 of 43
Print all the permutations of the given string
List every order the letters of a word can stand in, by choosing who goes first, then who goes second, and so on.
- Simple way
- time O(n^n * n)
room O(n) - Clever way
- time O(n * n!)
room O(n)
The problem, simply
A permutation is the same letters in a new order. abc can become acb, bac, bca, cab or cba, and itself. Print all of them.
With n different letters there are n! orders: n choices for the first spot, n - 1 for the second, and so on. If letters repeat, like aab, print each different order only once.
A story to remember it
Picture it
The choice tree for abc. Each level fixes one more spot, and the edge shows which letter went there. The highlighted path puts b first, then c, and ends at bca. The 6 bottom circles are the 6 permutations.
- answer
- path
Examples
A normal case
- In
s = 'abc'- Out
abc, acb, bac, bca, cba, cab
3 x 2 x 1 = 6 orders. This is the order the swap method prints them in.
Two letters
- In
s = 'ab'- Out
ab, ba
Two letters can stand two ways.
Repeated letters
- In
s = 'aab'- Out
aab, aba, baa
Swapping the two a's makes no new word, so there are only 3 different orders, not 6.
One letter
- In
s = 'z'- Out
z
One letter has one order.
Way 1: the simple way
For each of the n spots, try every position of the original string, even ones already used. That makes n^n fillings. At the end, keep only the fillings that used every position exactly once.
- Fill spot 0 with any position
0..n-1, then spot 1, and so on. - When all spots are filled, check that no position was used twice.
- If the check passes, build the word and keep it.
import java.util.*;
class Solution {
public List<String> permutations(String s) {
List<String> out = new ArrayList<>();
fill(s, new int[s.length()], 0, out);
return out;
}
private void fill(String s, int[] pick, int spot, List<String> out) {
int n = s.length();
if (spot == n) {
boolean[] used = new boolean[n];
StringBuilder word = new StringBuilder();
for (int p : pick) {
if (used[p]) return; // a friend in two spots, throw it away
used[p] = true;
word.append(s.charAt(p));
}
out.add(word.toString());
return;
}
for (int p = 0; p < n; p++) {
pick[spot] = p;
fill(s, pick, spot + 1, out);
}
}
}How long it takes: n^n fillings, each checked in n steps, so O(n^n * n). For 8 letters that is 16 million fillings for only 40,320 real answers. Extra room: O(n). It also prints repeats for aab.
Way 2: the clever way
Never build a bad filling in the first place. Fill spot i only with letters that are still free. A neat way to keep track: the free letters are always the ones at positions i and after. So to put letter j in spot i, swap a[i] and a[j], solve the rest from i + 1, then swap them back.
To skip repeats, remember which letters you already tried in spot i. Trying a second a there would only make the same words again.
- If
iis the last spot (or past it), the row is finished. Print it. - For each
jfromito the end: ifa[j]was already tried in spoti, skip it. - Swap
a[i]anda[j], recurse oni + 1, then swap back.
Swapping letters into place for abc
01 / 11
Start with abc. Position 0 is open. We will try each letter there, one at a time, by swapping it into position 0.
- found
- 0
- looking now
- seen
- done
- answer
- path
import java.util.*;
class Solution {
public List<String> permutations(String s) {
List<String> out = new ArrayList<>();
permute(s.toCharArray(), 0, out);
return out;
}
private void permute(char[] a, int i, List<String> out) {
if (i >= a.length - 1) { // zero or one spot left: done
out.add(new String(a));
return;
}
Set<Character> tried = new HashSet<>(); // letters already tried in spot i
for (int j = i; j < a.length; j++) {
if (!tried.add(a[j])) continue; // same letter, same words, skip
swap(a, i, j); // put a[j] in spot i
permute(a, i + 1, out);
swap(a, i, j); // undo
}
}
private void swap(char[] a, int i, int j) {
char t = a[i]; a[i] = a[j]; a[j] = t;
}
}How long it takes: there are n! finished rows, and making each one into a string costs n, so O(n * n!). The tree above the leaves adds less than that. Extra room: the recursion is n levels deep, and each level keeps a small set, so O(n) besides the letters, not counting the answers.
Watch out for
- With repeated letters, a set of all answers works too, but it still makes all
n!rows first. Skipping a letter already tried at this spot never makes the repeats at all. - The swap method does not print in dictionary order (
cbacomes beforecab). Sort first and use the "pick a free letter" method, or sort at the end, if order matters. n!grows very fast: 10 letters already give 3,628,800 orders.- Another common way keeps a
used[]array and a builder, and for each spot picks any unused letter. SameO(n * n!)time.
What interviewers ask next
- Print in dictionary order with repeats removed? Sort the letters, keep
used[], and skip letteriwhen it equals letteri - 1and letteri - 1is not used right now. - Next permutation in place? From the right, find the first
a[i] < a[i + 1], swapa[i]with the smallest bigger letter to its right, then reverse everything afteri.O(n). - What is the
kth permutation in order without listing them? Each first letter covers(n - 1)!permutations, so dividekby that to pick the first letter, and repeat. - Is
s2holding some permutation ofs1? Do not list them. Slide a window of lengths1.length()overs2with 26 letter counts.
Remember this
Grown-up words: the photo rows are permutations, the tree of choices is a recursion tree, swapping in and back out is backtracking, and n! is n factorial.