Showing posts with label String Manipulation. Show all posts
Showing posts with label String Manipulation. Show all posts

String: Permutations Using Recursion

Problem: Write the code for producing/printing permutations of the characters in a string. For example: If "abc" is the input string, output permutations should be "abc", "bac", "bca", "acb", "cab", "cba".
Solution: There are at least 2 approaches to solving this problem. Even though both approaches use recursion, there is a subtle difference between the two. The second approach uses more number of recursive calls than the first and my rough analysis has shown that run time of both approaches is almost same. The first approach would be preferable given that the there are only n recursive calls compared to n! recursive calls of the second approach. Approach 1:
Pseudo Code:

1. Set index = 0 to point to the 1st character in the input string
    2. If index = n-1 return last character (n is length of input string)
    3. Get Permutations of string starting at index + 1 
    4. For each permutation in the list from step 3
         a. Insert input[index] character in all possible positions of each
            permutation.

Example:
 input = "abc"
 get permutations for "bc": "bc" and "cb"
 insert "a" in all positions of both "bc" and "cb": 
          "a" * "bc": "abc", "bac", "bca"
          "a" * "cb": "acb", "cab", "cba"
Code (C#):
List<string> Permute(string, str, int startIndex)
{
   if(startIndex == str.Length -1 )
      return new string[]{str.Substring(startIndex)};

   List<string> permutations = Permute(str, ++startIndex);
   List<string> newPermutations = new List<string>();

   foreach(string permutation in permutations)
   {
      for(int i=0; i<permutation.Length; i++)
      {
         newPermutations.Add(permutation.Insert(i, str[startIndex]));
      }
   }
   return newPermutations;
}
Analysis: Number of recursive calls is equal to N (length of the input string). if L is the level of each recursive call, the run time for each recursive call is L!. So at the top most call, since L = N, it is N!. Total: N! + N-1! + N-2! ... + 1
Approach 2:
The idea here is to put each character in the string in the 1st position and combine it with the permutation of the characters in the rest of the string. As you can see this is also a recursive definition. Pseudo Code:

For i=0 to N
  1. Swap letters 0 and i.
  2. Permute letters 1 to N-1, printing or saving the entire string each time. 
Code (C):
Permute(char* inputStart, char* current)
{
   char *swap;
   char temp;

   if(*(current + 1) = '\0')
      printf("%s\n", inputStart);
   else
   {
      for(swap = current; *swap != '\0'; ++swap)
      {
         temp = *swap;
         *swap = *current;
         *current = temp;
         
         Permute(inputStart, current + 1);
         
         //revert the letters
         *current = *swap;
         *swap = temp;
      }
   }
}
Run time: This solution makes at least N! + N-1! + N-2!+ ... + 1 recursive calls doing 1 unit of work in each call. Compare this to the less number of recursive calls from the approach one, but approach one does increasing more work going back up from each recursive call.

String: Convert Integer to String itoa

// val: integer that needs to be converted to string representation
// destBuffer: buffer where the output string will be written
// radix: is the radix of the numbering system (10 for decimal, 16 for Hexadecimal,
// 2 for binary)

char * my_itoa(int val, char * destBuffer, int radix)
{
   if(val == 0)
   {
      destBuffer = "0";
      return destBuffer;
   }

   bool isNeg = false;
   // if input integer is negative set the isNeg flag and make it positive
   // we will do the conversion using the absolute value and then append the
   // the - sign in the end
   if(val < 0)
   {
      isNeg = true;
      val *= -1; 
   }

   char *currentDest = destBuffer;

   while(val > 0)
   {
     int digit = val % radix;
     val /= radix;
     *currentDest = (char) (digit + '0');
     currentDest++;
   }
 
   if(isNeg)
   {
     *currentDest = '-';
     currentDest++;
   }

   *currentDest = '\0';
   reverse(destBuffer);
   return destBuffer;
}

String: Convert String To Integer atoi

Problem: Implement atoi function in C language and give the test cases. atoi function converts a string to an integer. The function prototype is as follows:
         int my_atoi(const char str[]);
Solution: The key is to be able to find out the int value of a numeric character.
ASCII value of a numeric character - ASCII value of the character '0' = int value of numeric character.
Ex: '8' - '0' = 8

Code:
#define MIN_INT -2147483648
#define MAX_INT 2147483647

int my_atoi(const char str[])
{
    if(str == NULL) return 0;
    
    int len = strlen(str);
    
    if(len <= 0) return 0;

    int index = 0;
    
    //skip leading spaces
    while(str[index] == ' ') index++;

    bool isNeg = str[index] == '-';
    int outNum = 0;

    if(isNeg)
    {
        index++;
        // skip white space after the sign
        while(str[index] == ' ') index++;
    }

    while(index < len)
    {
        char currentChar = str[index++];
        if(currentChar >= '0' && currentChar <= '9')
        {
            int oldValue = outNum;
            int charVal = currentChar - '0';
            outNum *= 10;
            outNum += charVal;

            //overflow underflow detection
            if(outNum < oldValue)
            {
                if(isNeg)
                    outNum = MIN_INT;
                else
                    outNum = MAX_INT;
                return outNum;
            }
        }
        else
            break;
    }
    if(isNeg)
        outNum = outNum * -1;
    return outNum;
}
atoi Test Cases:
Input         : Output
""            : 0
"0"           : 0
"1"           : 1
"-1"          : -1
"10"          : 10
"-10"         : -10
"1234567890"  : 1234567890
"23 45"       : 23
" 99"         : 99
" -66"        : -66
"- 77"        : -77
"55 "         : 55
"-2147483648" : -2147483648 (MIN)
"2147483647"  : 2147483647  (MAX)
"2147483648"  : 2147483647   (overflow)
"-2147483649" : -2147483648  (underflow)
"abc*"        : 0
"23ab"        : 23
"23ab34"      : 23
"b31"         : 0

String: Remove Specified Characters

Problem: You are given 2 strings. The first string is the input string that needs to be cleaned (in place) and the second string contains characters that need to be removed from the the first string. For example if string1 = "teetotaller" and removeString= "ae" then the output (cleaned string) will look like "ttotllr".
Solution:
The naive approach is as follows:
  • Create an output buffer the same size of string1.
  • Loop through individual characters of string1 and check if they exist in the removeString.
  • Copy a character to the output buffer only if it doesn't exist in removeString.
  • Copy the contents of the output buffer to string1.
This solution can certainly be improved to be faster. There are 2 things we can improve in the proposed solution.

Improvement 1: The check to see if a character exists in the removeStr is O(m) where m is the size of the remove string. Hence the run time for loop from step 2 above is O(n * m).
If the check is somehow made to perform in O(1), the run time would be O(n). O(1) lookup can be done using a Hashtable or a flag array. Both techniques are compared in the Find First NonRepeated Character post. In this solution we'll use the array approach. We will assume that the character set is 7 bit ASCII, which means that there are 128 possible characters. The array of bools will be used to determine if a character is to be removed or not. The ASCII code value of the character itself will be used as an index in to the array. For example if 'a' is one of the characters in the remove string then removeArray['a'] will be set to true (removeArray['a'] = true;).

Improvement 2: The above proposed solution uses an output buffer to write the clean output characters to and then copies the output back to the original string. We don't really need the second buffer, if we just copy the characters to the input string instead of the output buffer. We will need to maintain a destination index to mark the spot where the next clean character needs to go. This improvement removes the need for the extra memory needed by the output buffer and also the need to copy it back to the original string.

With the help of above two improvements the run time is O(n). We now need extra memory for the array but its constant (not tied to n).

Improved Solution:
  • Loop through individual characters of string1 and check if they exist in the removeString.
  • Copy a character back to the input string only if it doesn't exist in removeString.
  • Terminate the string with a NULLCHAR ('\0').
//Removes the specified characters from an input string
void RemoveCharacters(char str[], char remove[])
{
int strLen = strlen(str);
int removeStrLen = strlen(remove);

bool removeCharacterFlags[128] = {false}; // assume ASCII character set

// set the flag for characters present in the remove string
for(int i=0; i<removeStrLen; i++)
{
    removeCharacterFlags[remove[i]] = true;
}

int destIndex = 0;      // index within the input string where the next
                        // clean character will go.
for(int i=0; i<strLen; i++)
{
    if(!removeCharacterFlags[str[i]])
    {
        str[destIndex++] = str[i];
    }
}
str[destIndex] = '\0';
}

String: Find First Non-Repeated Character

Problem: Find the 1st non-repeated character in a string. Example: teetotaller The 1st non-repeated character is o.

Solution: The solution involves keeping track of what characters in the string have a count of more than one. The choice of data structure we will use depends on the type of string. If the string is an ASCII string with 256 possible values then an array of size 256 would be sufficient to track the count of each character. But, if the string is a Unicode string with 65536 possible character values and the input string was a small string (few characters wide), using an array would be inefficient . In the case where our input string is relatively small and the character set is large we can use a HashTable instead. If the loading factor of the Hashtable was selected to be high (to save memory) it could potentially suffer from collisions, but since out string is small the chances of collisions are less. On the other hand if the string was a long string and the character set was small the array based solution would be more efficient memory wise.
// returns the index of the 1st non-repeated character in the input string
int Find_First_Non_Repeated_Char(string s)
{
     Hashtable ht = new Hashtable();

     // populate the hash table with count for each character in the string
     for(int i=0; i<s.Length; i++)
     {
        if(ht.Contains(s[i]))
        {
           int count = (int) ht[s[i]]; //get the count for the character
           ht[s[i]] = ++count;
        }
        else
        {
           ht[s[i]] = 1;
        }
     }

     // now go through the hash table one character at a time and find the  
     // one that has a count of 1
     
     for(int i=0; i< s.Length; i++)
     {
        if(ht.Contains(s[i]) && (int)ht[s[i]] == 1)
        {
           return i;
        }
     }
     return -1; // the input does not contain non-repeated character
}

String: Palindrome Check

Problem: Find if a given string is a palindrome. Palindrome is a word or a phrase that reads the same in either direction.
Ex: A man, a plan, a canal, panama!
Punctuation and spaces can be ignored.

Solution: The solution is pretty straight forward and involves comparing the characters at both ends, incrementally moving towards the center of the string. This is similar logic we used for reversing a string in place. The IsPalindrome method implemented below assumes that the string has been cleaned off of the punctuation characters and spaces.

Code:
bool IsPalindrome(char str[])
{
    int len = strlen(str);

    for(int i=0, j=len-1; i<j; i++, j--)
    {
        if(str[i] != str[j])
            return false;
    }
   return true;
}

String: Reverse Words

Problem: Reverse the words in a sentence. For example "Hello World" should become "World Hello". Comment: This is one of most frequently asked interview questions on string manipulation.

Solution: First we reverse the entire string and then reverse each word of this reversed string.
"Hello World"
      |
   reverse
      |
      v
"dlroW olleH"
  |      |
reverse reverse
  |      |
  v      v
"World Hello"
Code:
void reverse(char str[], int beginIndex, int endIndex)
{
  while(beginIndex < endIndex) // keep swaping characters as long as
                               // begin index is less than end index
  {
     // swap the characters
     char temp = str[beginIndex]; 
     str[beginIndex] = str[endIndex];
     str[endIndex] = temp;

     beginIndex++; //increment the begin index
     endIndex--; //decrememnt the end index
  }
}

void reverse_words(char str[])
{
  reverse(str, 0, strlen(str)-1);
  int currentIndex = 0;
  int wordBeginIndex = 0;
  int wordEndIndex = -1;

  while(str[currentIndex])
  {
     if(str[currentIndex + 1] == ' '  // if we are at the word
        || str[currentIndex + 1] == '\0') // boundary or end of the string
     {
        wordEndIndex = currentIndex;
        reverse(str, wordBeginIndex, wordEndIndex);
        wordBeginIndex = currentIndex + 2;
     }
     currentIndex++;
  }
}

String: Reverse in place

Problem: Given a string of unknown length reverse it in place.

Solution: Lets take a sample string "hello". When reversed it should read as "olleh". For those who are new to C/C++, a string has null terminator at the end. This null terminator denotes the end of the string. So, when you say declare a string in C language like: char *str = "hello";
How many bytes of memory does it take to store that string? 5 (for the characters) +1 (for the null). The representation of the string in memory looks like this:
Now, the reverse string should look like as shown below: If you notice, we just need to swap the first and the last characters, the 2nd and the 2nd last, 3rd and 3rd last and so on.

Code:
int main(int argc, char* argv[])
{
   char str[7] = "hello"; // create an array of characters of size 7
                          // and assign the string hello to it.
                          // Last char will be '\0' (null terminator)
   printf("%s", str);       // this will print hello
   reverse(str);
   printf("%s", str);     // this will print olleh
   return 0;
}

void reverse(char * str)
{
   char * begin = str;
   char * end = str;

   // position the end pointer at the last character
   while(*(end+1) != NULL)
   {
       end++;
   }

   while(begin < end) // as long as begin pointer is less than end pointer
 {
  // swap the characters
  char temp = *begin; 
  *begin = *end;
  *end = temp;

  begin++; //increment the begin ptr
  end--;  //decrememnt the end pointer
 }
}