Take only letters from the string and reverse them

680 Views Asked by At

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?

3

There are 3 best solutions below

2
Lance U. Matthews On BEST ANSWER

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"
0
hassaan mustafa On

Hey you can do something like:

string test = "test12pop90java989python", tempStr = "", finalstr = "";
            var charArray = test.ToCharArray();

            for (int i = 0; i < charArray.Length; i++)
            {
                if (!Char.IsNumber(charArray[i]))
                {
                    tempStr += charArray[i];
                }
                else
                {
                    char[] ReverseString = tempStr.Reverse().ToArray();
                    foreach (char charItem in ReverseString)
                    {
                        finalstr += charItem;
                    }

                    tempStr = "";
                    finalstr += charArray[i];
                }
            }

            if(tempStr != "" && tempStr != null)
            {
                char[] ReverseString = tempStr.Reverse().ToArray();
                foreach (char charItem in ReverseString)
                {
                    finalstr += charItem;
                }

                tempStr = "";
            }

I hope this helps

0
Aleksa Majkic On

A possible solution using Regex and Linq:

using System;
using System.Text.RegularExpressions;
using System.Linq;
                
public class Program
{
    public static void Main()
    {
        var result = "";
        var matchList = Regex.Matches("test12pop90java989python", "([a-zA-Z]*)(\\d*)");
        var list = matchList.Cast<Match>().SelectMany(o =>o.Groups.Cast<Capture>().Skip(1).Select(c => c.Value));
        foreach (var el in list)
        {
            if (el.All(char.IsDigit))
            {
                result += el;
            }
            else
            {
                result += new string(el.Reverse().ToArray());
            }
        }

        Console.WriteLine(result);
    }
}

I've used code from stackoverflow.com/a/21123574/1037948 to create a list of Regex matches on line 11:

var list = matchList.Cast<Match>().SelectMany(o =>o.Groups.Cast<Capture>().Skip(1).Select(c => c.Value));