简体   繁体   中英

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.

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;
}

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;
    }

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;
}

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));
}

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();
 }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM