String · 41 of 43
Transform One String to Another using Minimum Number of Given Operation
Turn one word into another by picking up letters and putting them at the front, using as few moves as possible.
- Simple way
- time O(n^2)
room O(1) - Clever way
- time O(n)
room O(1)
The problem, simply
You get two words, A and B. There is only one move: pick any letter of A and put it at the very front of A. Find the smallest number of moves that turns A into B.
If it can never work, give back -1. That happens when the two words do not have exactly the same letters (same letters, same counts).
A story to remember it
Picture it
A = EACBD must become B = EABCD. C and D can stay where they are. E, A and B each get called to the front once: 3 moves.
- done
- rejected
Examples
One move
- In
A = 'ABD', B = 'BAD'- Out
1
Pick up B and put it at the front: BAD.
A normal case
- In
A = 'EACBD', B = 'EABCD'- Out
3
C and D can stay. Move B, then A, then E to the front: BEACD, ABECD, EABCD.
Repeated letters
- In
A = 'AAB', B = 'ABA'- Out
2
Only the last A can stay at the back. Move B to the front (BAA), then move the first A to the front (ABA).
Already equal
- In
A = 'ABC', B = 'ABC'- Out
0
Nobody needs to move.
Impossible
- In
A = 'ABC', B = 'ABD'- Out
-1
A has no D, and moves never create new letters.
Way 1: the simple way
The kids who are never moved keep their old order and end up at the back. So they form the end of B, and they appear in A in the same order (maybe with other kids between them). In grown-up words, they are a suffix of B that is a subsequence of A.
- First check that
AandBhave the same letters with the same counts. If not, return-1. - Try to keep
keep = nkids, thenn - 1, and so on. - For each
keep, check if the lastkeepletters ofBappear inAin order. - The first
keepthat works is the most kids you can leave alone. The answer isn - keep.
class Solution {
public int transform(String A, String B) {
if (!sameLetters(A, B)) return -1;
int n = A.length();
for (int keep = n; keep >= 0; keep--) {
if (inOrder(B.substring(n - keep), A)) return n - keep; // everyone else moves once
}
return n;
}
// do the letters of small appear in big, in the same order?
private boolean inOrder(String small, String big) {
int k = 0;
for (int i = 0; i < big.length() && k < small.length(); i++) {
if (big.charAt(i) == small.charAt(k)) k++;
}
return k == small.length();
}
private boolean sameLetters(String A, String B) {
if (A.length() != B.length()) return false;
int[] count = new int[256];
for (char c : A.toCharArray()) count[c]++;
for (char c : B.toCharArray()) count[c]--;
for (int x : count) if (x != 0) return false;
return true;
}
}How long it takes: up to n + 1 tries, each one a walk over A, O(n^2). Extra room: a fixed table of 256 counts, O(1).
Way 2: the clever way
Instead of trying every length, find the longest end of B in one walk. Put one finger i at the end of A and one finger j at the end of B, and walk left.
- Check the letter counts. If they differ, return
-1. - If
A[i]equalsB[j], that kid can stay. Move both fingers left. - If not, the kid at
A[i]must be called to the front. Count one move, and move onlyileft. - When
ifalls off the left end, the count is the answer.
Keeping a kid as soon as it fits is always safe. Matching from the right and taking the first fit never uses up a letter that a longer match would have needed.
Walk both lines from the back
01 / 09
First check the letters. Both words have one each of A, B, C, D and E, so it is possible.
- checking
- done
- answer
- rejected
- just added
class Solution {
public int transform(String A, String B) {
int n = A.length();
if (n != B.length()) return -1;
int[] count = new int[256];
for (char c : A.toCharArray()) count[c]++;
for (char c : B.toCharArray()) count[c]--;
for (int x : count) if (x != 0) return -1; // different letters: impossible
int moves = 0;
int j = n - 1; // finger at the back of B
for (int i = n - 1; i >= 0; i--) { // finger at the back of A
if (A.charAt(i) == B.charAt(j)) j--; // this kid fits and stays
else moves++; // this kid must go to the front
}
return moves;
}
}How long it takes: one walk over each word, O(n). Extra room: the fixed count table, O(1).
Watch out for
- Walking from the front. Kids who stay end up at the back, so the matching must start at the back.
- Moving
jon a mismatch. On a mismatch onlyimoves, because that letter ofAis the one that leaves. - Skipping the count check. Without it,
ABCandABDwould give1instead of-1. - Lengths that differ. Return
-1right away.
What interviewers ask next
- Print the moves, not only the count. The kids that did not fit must form the front of
B. Call them to the front in the reverse of that order. - What if the move puts a letter at the back instead? Mirror the idea: walk from the front and keep the longest start of
Bthat appears inAin order. - What if you may move a letter anywhere? Then the kids who stay only need to keep their order, so the answer is
nminus the longest common subsequence ofAandB, which needs a 2D table. - Why is greedy matching safe? Taking the rightmost possible match for each letter of
Bleaves the most room on the left for the letters still to match.
Remember this
Grown-up words: the kids who stay are the longest suffix of B that is a subsequence of A, the two fingers are two pointers, keeping the first fit is a greedy choice, and the letter counts are a frequency table.