【问题标题】:Check whether string in palindrome : C# code efficiency检查字符串是否在回文中:C# 代码效率
【发布时间】:2019-06-01 19:03:54
【问题描述】:

我来自 C 背景,最近开始使用 C# 编写代码,所以请不要介意我的问题是否有点基础。基本上,我想编写一个函数,如果字符串是回文则返回 true,否则返回 false。

字符串可能包含空格、','、':' 等我必须忽略的字符。我已经编写了如下代码

    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;

    }

将使用以下字符串调用函数的位置

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

我在文档中读到,在 C# 中,字符串是不可变的,因此每次我执行替换或 ToString 之类的操作时,都会创建一个新副本。

所以我想检查一下 一世)。这段代码有效吗? ii)。如果没有,如何提高效率。

【问题讨论】:

  • 不要把一个字符转成字符串,然后比较两个字符转成字符串。将字符(一个简单的 16 位数值)转换为字符串(将在堆上分配并接受垃圾回收的对象)可能是您可以做的最昂贵的事情,如果您真正想要的是比较到字符...
  • 为了提高效率,您可以在检查回文的循环中跳过它们,而不是替换字符(这需要遍历字符串 3 次)。
  • 如果你想以不区分大小写的方式比较字符,在比较之前将字符转换为大写或小写(char/System.Char有这样的方法)
  • 您的代码可以通过使用while 循环和两个指向字符串开头和结尾的变量来提高效率。增加起始值,减少结束值并跳过您不感兴趣的字符。
  • 但这几乎可以肯定是一个家庭作业问题,也许你应该问你的导师?

标签: c# palindrome


【解决方案1】:

您无需使用.Replace 或创建新字符串,您可以在比较时跳过不需要的字符。

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

【讨论】:

    【解决方案2】:

    与您编写的 for loop 相比,这对于 Plaindrome 检测来说更具可读性:

    由于Array.Reverse 颠倒了元素的顺序,这是一种简短但不一定高效的方法:

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

    需要更多代码行的更有效方法是:

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

    另一种方法类似于第二种方法,但不需要将char 转换为String 进行比较:

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

    【讨论】:

    • 谢谢本杰明。绝对你给出的解决方案更干净。
    • 您可以保留 s.Replace 来删除字符串中的空格和 '。
    • 在性能方面效率不高(不批评您的答案,只是提及它,因为该问题也与“效率”有关)。但它可读可维护,所以没关系...
    • 我认为 for 循环最好反向创建第二个数组
    • 好多了,但您不会跳过被忽略的字符(空格、逗号等)
    【解决方案3】:

    强制性的简短但低效的 LINQ 解决方案:

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

    【讨论】:

    • 如果Reverse 对字符串有特殊的实现,那么效率不会那么低。
    • @SamuelVidal 不幸的是it hasn't。实现起来很简单,但是编写更多代码会破坏保持简短的目的。 ?
    • 酷,很高兴知道,你是对的.Net Source line 906。至少你可以把 Take 放在 Reverse 之前。
    • @SamuelVidal 最初我将Take 放在Where 之前,但对于像"abb..a" 这样的不平衡回文则失败了。 ?
    【解决方案4】:

    如果您有 C 背景,那么您应该熟悉此解决方案。无需分配一堆新字符串;忽略非字母字符。

    在 .NET 中似乎没有对 char 值进行不区分大小写的比较,因此此代码假定 ToLower(...) 与当前文化就足够了。

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

    【讨论】:

      猜你喜欢
      • 2015-10-02
      • 1970-01-01
      • 2017-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-30
      • 2012-04-05
      相关资源
      最近更新 更多