This is an interview problem that I am stuck on:
Given a string consisting of a, b and c\'s, we can perform the following operation: Take any two adjacent
This problem also appears in HackerRank as an introduction to Dynamic Programming. Even though there are nice close-form solution as many posters have already suggested, I find it helpful to still work it out using the good-old dynamic programming way. i.e. find a good recurrence relation and cache intermediate results to avoid unnecessary computations.
As some people have already noted, the brute-force method of iterating through consecutive letters of the input string and all the resulting reduced strings will not work when input string is long. Such solution will only pass one test-case on HackerRank. Storing all reduced strings are also not feasible as the number of such string can grow exponentially. I benefit from some people's comments that the order of the letters does not matter, and only the numbers of each letter matter.
Each string can be reduced as long as it has more than one of two distinct letters. Each time a string is reduced, 1 of each of the distinct letters goes away and a letter of the third kind is added to the string. This gives us an recurrence relation. Let f(a,b,c)
be the length of the smallest string given a
of the letter 'a', b
of the letter 'b', and c
of the letter 'c' in the input string, then
f(a,b,c) = min(f(a-1,b-1,c+1), f(a-1,b+1,c-1), f(a+1,b-1,c-1));
since there are three possibilities when we reduce a string. Of course, every recurrence relation is subject to some initial conditions. In this case, we have
if(a < 0 || b < 0 || c < 0)
return MAX_SIZE+1;
if(a == 0 && b == 0 && c == 0)
return 0;
if(a != 0 && b == 0 && c == 0)
return a;
if(a == 0 && b != 0 && c == 0)
return b;
if(a == 0 && b == 0 && c != 0)
return c;
here MAX_SIZE
is the maximum number of a given letter in the HackerRank problem. Anytime we run out of a given letter, the maximum size is returned to indicate that this string reduction is invalid. We can then compute the size of the smallest reduced string using these initial conditions and the recurrence relation.
However, this will still not pass the HackerRank test cases. Also, this incurs too many repeated calculations. Therefore, we want to cache the computed result given the tuple (a,b,c)
. The fact that we can cache the result is due to the fact that the order of the letters does not change the answer, as many of the posts above have proved.
My solution is posted below
#include
#include
#include
#include
#include
#define MAX_SIZE 101
int cache[MAX_SIZE][MAX_SIZE][MAX_SIZE];
void init_cache() {
for(int i = 0 ; i < MAX_SIZE; i++) {
for (int j = 0; j < MAX_SIZE; j++) {
for(int k = 0; k < MAX_SIZE; k++)
cache[i][j][k] = -1;
}
}
}
void count(char* array, int* a, int* b, int* c) {
int len = strlen(array);
for(int i = 0; i < len; i++) {
if(array[i] == 'a')
(*a)++;
else if(array[i] == 'b')
(*b)++;
else
(*c)++;
}
}
int solve(int a, int b, int c) {
if(a < 0 || b < 0 || c < 0)
return MAX_SIZE+1;
if(a == 0 && b == 0 && c == 0)
return 0;
if(a != 0 && b == 0 && c == 0)
return a;
if(a == 0 && b != 0 && c == 0)
return b;
if(a == 0 && b == 0 && c != 0)
return c;
if(cache[a][b][c] != -1) {
return cache[a][b][c];
}
int ci = solve(a-1, b-1, c+1);
int bi = solve(a-1, b+1, c-1);
int ai = solve(a+1, b-1, c-1);
if(a > 0 && b > 0)
cache[a-1][b-1][c+1] = ci;
if(a > 0 && c > 0)
cache[a-1][b+1][c-1] = bi;
if(b > 0 && c > 0)
cache[a+1][b-1][c-1] = ai;
return ci < bi ? (ci < ai ? ci : ai) : (ai < bi ? ai : bi);
}
int main() {
int res, T, i;
scanf("%d", &T);
assert(T<=100);
char arr[100001];
init_cache();
for(i = 0; i < T; i++) {
scanf("%s",arr);
int a = 0;
int b = 0;
int c = 0;
count(arr, &a, &b, &c);
int len = solve(a, b, c);
printf("%d\n", len);
}
return 0;
}