'Check whether string in palindrome : C# code efficiency

I am coming from C background and recently started writing in C# so please don't mind if my question seems somewhat basic. Basically, i want to write a function which will return true if string is palindrome and false if not.

The string may contain characters like space, ',', ':' which i have to ignore. I have written the code as below

    static bool IsPalindrome(string s)
    {
        s = s.Replace(" ", "");
        s = s.Replace(",", "");
        s = s.Replace(":", "");

        int j = s.Length - 1;

        for(int i = 0; i < s.Length/2; i++)
        {
            if(s[i].ToString().Equals(s[j].ToString(),StringComparison.InvariantCultureIgnoreCase))
            {
                j--;
            }
            else
            {
                return false;
            }
        }
        return true;

    }

where the function will be invoked with the following string

string s = "A man, a plan, a canal: Panama";

I read in the documentation that in C#, string are immutable so each time i do a operation like replace or ToString, a new copy is getting created.

So i wanted to check that i). Is this code efficient? ii). If not, how to make it more efficient.



Solution 1:[1]

You don't need to use .Replace or create new strings, you can just skip the unwanted characters as you compare.

static bool IsPalindrome(string s)
{
    var i = 0;
    var j = s.Length - 1;
    while (j > i)
    {
        if (s[i] == ':' || s[i] == ',' || s[i] == ' ')
        {
            i++;
            continue;
        }
        if (s[j] == ':' || s[j] == ',' || s[j] == ' ')
        {
            j--;
            continue;
        }

        if (char.ToUpperInvariant(s[i++]) != char.ToUpperInvariant(s[j--])) return false;
    }

    return true;
}

Solution 2:[2]

This would be more readable approach for the Plaindrome detection comparing to the for loop you have written:

A short approach but not necessary efficient due to Array.Reverse which Reverses the order of the elements :

static bool IsPalindrome(string s)
{
    s = s.Replace(" ", "");
    s = s.Replace(",", "");
    s = s.Replace(":", "");

    char[] array = s.ToCharArray();
    Array.Reverse(array);
    string backwards = new string(array);

    return s == backwards;
}

A more efficient approach which requires more lines of coding would be :

    static bool IsPalindrome(string s)
    {
        s = s.Replace(" ", "");
        s = s.Replace(",", "");
        s = s.Replace(":", "");

        int i = 0;
        int j = s.Length - 1;

        while (i < j)
        {
            if (s[i].ToString().ToLower() != s[j].ToString().ToLower())
                return false;

            i++;
            j--;
        }
        return true;
    }

Another approach similar to the 2nd one but without the needs of converting the char into String for comparison:

    static bool IsPalindrome(string s)
    {
        s = s.Replace(" ", "");
        s = s.Replace(",", "");
        s = s.Replace(":", "");

        int i = 0;
        int j = s.Length - 1;

        while (i < j)
        {

            if (!char.ToLower(s[i]).Equals(char.ToLower(s[j])))
                return false;

            i++;
            j--;
        }
        return true;
    }

Solution 3:[3]

The obligatory short but inefficient LINQ solution:

static bool IsPalindrome(string s)
{
    return s.Where(Char.IsLetterOrDigit).Take(s.Length / 2)
        .SequenceEqual(s.Reverse().Where(Char.IsLetterOrDigit).Take(s.Length / 2));
}

Solution 4:[4]

This solution should be familiar to you if you're coming from a C background. There's no need to allocate a bunch of new strings; just ignore non-letter characters.

There doesn't seem to be a case-insensitive comparison of char values in .NET, so this code assumes that ToLower(...) with the current culture is sufficient.

public static bool EqualsIgnoreCase(char c1, char c2)
{
    var culture = System.Globalization.CultureInfo.CurrentCulture;
    return Char.ToLower(c1, culture) == Char.ToLower(c2, culture);
}

public static bool IsPalindrome(string s)
{   
    switch (s?.Length ?? 0)
    {
        case 0:
            return false;
        case 1:
            return true;
        case 2:
            return EqualsIgnoreCase(s[0], s[1]);
        case 3:
            return EqualsIgnoreCase(s[0], s[2]);
    }

    var firstIndex = 0;
    var lastIndex = s.Length - 1;

    // todo: this probably falls on its face for a string with only non-letters
    do
    {
        while (!Char.IsLetter(s[firstIndex]))
            ++firstIndex;
        while (!Char.IsLetter(s[lastIndex]))
            --lastIndex;
        if (!EqualsIgnoreCase(s[firstIndex++], s[lastIndex--]))
            return false;

    } while (firstIndex < lastIndex);

    return true;
}

Solution 5:[5]

This program upon execution counts the number of palindromes words/numbers that are present in the sentence and displays them.

class Program
{

  static void Main(string[] args)
  {
    Palindrome("Lorem ipsum dolor sit amet, voluptate mom velit omo esse cillum dolore eu fugiatnulla pariatur. Excepteur non proident est laborum.");
  }

  static void Palindrome(string sentence)
  {
     var arr = sentence.Split(" ");
     List<string> palindromes = new List<string>();
     int count = 0;
     foreach (var str in arr)
      {
        string rev = string.Empty;
        for (int i = str.Length-1; i >= 0; i--)
         {
           rev = rev+str[i];
         }
        if (str.Equals(rev,StringComparison.OrdinalIgnoreCase))
         {
           palindromes.Add(rev);
           count++;
         }
              
     }
     Console.WriteLine($"Total palindromes: {count}");
     foreach (var palindrome in palindromes)
      {
        Console.Write($"'{palindrome}' ");
      }
      Console.ReadKey();
 }

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ahmad Ibrahim
Solution 2
Solution 3 Theodor Zoulias
Solution 4
Solution 5 Abah Joseph Israel