Search code examples
c#.netstringreversetext-manipulation

Take only letters from the string and reverse them


I'm preparing for my interview, faced the problem with the task. The case is that we're having a string: test12pop90java989python

I need to return new string where words will be reversed and numbers will stay in the same place:

test12pop90java989python ==> tset12pop90avaj989nohtyp

What I started with:

  1. Transferring string to char array

  2. Use for loop + Char.IsNumber

  3. ??

     var charArray = test.ToCharArray();
     for (int i = 0; i < charArray.Length; i++)
     {
         if (!Char.IsNumber(charArray[i]))
         {
         ....
         }
     }
    

but currently I'm stuck and don't know how to proceed, any tips how it can be done?


Solution

  • You can't reverse a run of letters until you've observed the entire run; until then, you need to keep track of the pending letters to be reversed and appended to the final output upon encountering a number or the end of the string. By storing these pending characters in a Stack<> they are naturally returned in the reverse order they were added.

    static string Transform(string input)
    {
        StringBuilder outputBuilder = new StringBuilder(input.Length);
        Stack<char> pending = new Stack<char>();
    
        foreach (char c in input)
            if (char.IsNumber(c))
            {
                // In the reverse order of which they were added, consume
                // and append pending characters as long as they are available
                while (pending.Count > 0)
                    outputBuilder.Append(pending.Pop());
    
                // Alternatively...
                //foreach (char p in pending)
                //  outputBuilder.Append(p);
                //pending.Clear();
    
                outputBuilder.Append(c);
            }
            else
                pending.Push(c);
    
        // Handle pending characters when input does not end with a number
        while (pending.Count > 0)
            outputBuilder.Append(pending.Pop());
    
        return outputBuilder.ToString();
    }
    

    A similar but buffer-free way is to do it is to store the index of the start of the current run of letters, then walk back through and append each character when a number is found...

    static string Transform(string input)
    {
        StringBuilder outputBuilder = new StringBuilder(input.Length);
        int lettersStartIndex = -1;
    
        for (int i = 0; i < input.Length; i++)
        {
            char c = input[i];
    
            if (char.IsNumber(c))
            {
                if (lettersStartIndex >= 0)
                {
                    // Iterate backwards from the previous character to the start of the run
                    for (int j = i - 1; j >= lettersStartIndex; j--)
                        outputBuilder.Append(input[j]);
                    lettersStartIndex = -1;
                }
    
                outputBuilder.Append(c);
            }
            else if (lettersStartIndex < 0)
                lettersStartIndex = i;
        }
    
        // Handle remaining characters when input does not end with a number
        if (lettersStartIndex >= 0)
            for (int j = input.Length - 1; j >= lettersStartIndex; j--)
                outputBuilder.Append(input[j]);
    
        return outputBuilder.ToString();
    }
    

    For both implementations, calling Transform() with...

    string[] inputs = new string[] {
        "test12pop90java989python",
        "123test12pop90java989python321",
        "This text contains no numbers",
        "1a2b3c"
    };
    
    for (int i = 0; i < inputs.Length; i++)
    {
        string input = inputs[i];
        string output = Transform(input);
    
        Console.WriteLine($" Input[{i}]: \"{input }\"");
        Console.WriteLine($"Output[{i}]: \"{output}\"");
        Console.WriteLine();
    }
    

    ...produces this output...

     Input[0]: "test12pop90java989python"
    Output[0]: "tset12pop90avaj989nohtyp"
    
     Input[1]: "123test12pop90java989python321"
    Output[1]: "123tset12pop90avaj989nohtyp321"
    
     Input[2]: "This text contains no numbers"
    Output[2]: "srebmun on sniatnoc txet sihT"
    
     Input[3]: "1a2b3c"
    Output[3]: "1a2b3c"