C++     Dynamic Programming     Greedy     Medium     Stack     String    

Problem Statement:

Given a string word to which you can insert letters "a", "b" or "c" anywhere and any number of times, return the minimum number of letters that must be inserted so that word becomes valid.

A string is called valid if it can be formed by concatenating the string "abc" several times.

 

Example 1:

Input: word = "b"
Output: 2
Explanation: Insert the letter "a" right before "b", and the letter "c" right next to "a" to obtain the valid string "abc".

Example 2:

Input: word = "aaa"
Output: 6
Explanation: Insert letters "b" and "c" next to each "a" to obtain the valid string "abcabcabc".

Example 3:

Input: word = "abc"
Output: 0
Explanation: word is already valid. No modifications are needed. 

 

Constraints:

  • 1 <= word.length <= 50
  • word consists of letters "a", "b" and "c" only. 

Solution:

First approach: This is a very simple case-by-case approach. Since the number of unique characters in word is 3, we can enumerate all cases and handle them separately. If we encounter an "abc" then we can simply move ahead 3 positions. If we see one of "ab", "bc", "ac", we can move ahead two steps and increment answer by one. In all other casaes, we need to increment answer by one and move ahead single step.

 
int addMinimum(string word) 
{
    int n = word.length(), i=0, res=0;
    while (i<n)
    {
        if (i+2<n && word[i]=='a' & word[i+1]=='b' & word[i+2]=='c')
            i+= 3;
        else if (i+1<n && word[i]=='a' && word[i+1]=='b') 
            {res+=1; i+=2;}
        else if (i+1<n && word[i]=='b' && word[i+1]=='c') 
            {res+=1; i+=2;}
        else if (i+1<n && word[i]=='a' && word[i+1]=='c') 
            {res+=1; i+=2;}
        else {res+=2; i+=1;}
    }
    return res;
}
 

Second approach: We can improve above logic and make our code more elegant but it does essentially the same thing.

 
int addMinimum(string word) 
{
    int n=word.length(), i=0, res=0;
    while (i<n)
    {
        int ctr = 0;
        if (word[i]=='a'){ctr++; i++;}
        if (word[i]=='b'){ctr++; i++;}
        if (word[i]=='c'){ctr++; i++;}
        res += (3-ctr);
    }
    return res;
}
 

Third approach: We can make the code even more elegant by treating word as a subsequence of another string which is just "abc" repeated a number of times, say lenby3 so that the length of the subsequence is 3*lenby3. Our answer is the difference in the lengths of this subsequence and of word. To find lenby3, the logic is that after the first character, it has to increment every time current character is no more than the previous.

 
int addMinimum(string word) 
{
    char prev='d';
    int n=word.length(), lenby3=0;
    for (int i=0;  i<n; i++)
    {
        lenby3 += (word[i]<=prev);
        prev = word[i];
    }
    return 3*lenby3-word.length();
}
 

All 3 approaches have $O(n)$ TC and $O(1)$ SC.