【问题标题】:Get count of unique characters between first and last letter获取第一个和最后一个字母之间的唯一字符数
【发布时间】:2021-06-23 16:33:22
【问题描述】:

我正在尝试获取单词的第一个字母和最后一个字母之间的唯一字符数。例如:如果我输入 Yellow 预期的输出是 Y3w,如果我输入 People,输出应该是 P4e,如果我输入 Money,输出应该是 M3y。这是我尝试过的:

//var strArr = wordToConvert.Split(' ');
string[] strArr = new[] { "Money","Yellow", "People" };
List<string> newsentence = new List<string>();

foreach (string word in strArr)
{
    if (word.Length > 2)
    {
        //ignore 2-letter words
        string newword = null;

        int distinctCount = 0;
        int k = word.Length;
        int samecharcount = 0;
        int count = 0;

        for (int i = 1; i < k - 2; i++)
        {
            if (word.ElementAt(i) != word.ElementAt(i + 1))
            {
                count++;
            }
            else
            {
                samecharcount++;
            }
        }
        distinctCount = count + samecharcount;

        char frst = word[0];
        char last = word[word.Length - 1];
        newword = String.Concat(frst, distinctCount.ToString(), last);
        newsentence.Add(newword);
    }
    else
    {
        newsentence.Add(word);
    }
}

var result = String.Join(" ", newsentence.ToArray());

Console.WriteLine("Output: " + result);
Console.WriteLine("----------------------------------------------------");

使用此代码,我得到了 Yellow 的预期输出,但似乎不适用于 People 和 Money。我可以做些什么来解决这个问题,或者我想知道是否有更好的方法来做到这一点,例如使用 LINQ/Regex。

【问题讨论】:

  • 为什么 Yellow 不会产生 Y4w?
  • @Maxqueue 因为程序应该只计算第一个和最后一个字符之间的唯一字符,在这种情况下 *ello 包含双 L,所以它只计算一次。
  • Linq 你可以简单地做一个像var result = word.First().ToString() + word.Substring(1, word.Length - 2).ToLower().Distinct().Count().ToString() + word.Last().ToString();这样的1 liner
  • 您要么增加计数,要么增加相同的字符计数,最终将其汇总为不同的计数。然而,通过你实现它的方式,你总是会得到 distinctCount 等于 k-2。您可能只想总结我假设的其中一个

标签: c# .net linq console-application


【解决方案1】:

这是一个使用 Linq 的实现:

string[] strArr = new[]{"Money", "Yellow", "People"};
List<string> newsentence = new List<string>();
foreach (string word in strArr)
{
    if (word.Length > 2)
    {
        // we want the first letter, the last letter, and the distinct count of everything in between
        var first = word.First();
        var last = word.Last();
        var others = word.Skip(1).Take(word.Length - 2);

        // Case sensitive
        var distinct = others.Distinct();

        // Case insensitive
        // var distinct = others.Select(c => char.ToLowerInvariant(c)).Distinct();

        string newword = first + distinct.Count().ToString() + last;
        newsentence.Add(newword);
    }
    else
    {
        newsentence.Add(word);
    }
}

var result = String.Join(" ", newsentence.ToArray());
Console.WriteLine(result);

输出:

M3y Y3w P4e

请注意,这不考虑大小写,因此FiIsSh 的输出为 4。

【讨论】:

  • 这就像一个魅力!只是为了完成所有操作,如何将 var others 转换为小写以避免 YeLlow 的输出 4
【解决方案2】:

也许不是最高效的,但这是另一个使用 linq 的示例:

var words = new[] { "Money","Yellow", "People" };
var transformedWords = words.Select(Transform);
var sentence = String.Join(' ', transformedWords);

public string Transform(string input)
{
    if (input.Length < 3)
    {
        return input;
    }
    var count = input.Skip(1).SkipLast(1).Distinct().Count();
    return $"{input[0]}{count}{input[^1]}";
}

【讨论】:

  • 我忘了SkipLast :)
  • @stuartd 是的,我用的不多。我认为这是最新添加的内容之一。
  • 你说得对,它是 .Net Core 中的新功能。前几天我第一次使用它。
【解决方案3】:

您可以在 Linq 的帮助下实现它。例如(C# 8+)

private static string EncodeWord(string value) => value.Length <= 2
  ? value
  : $"{value[0]}{value.Substring(1, value.Length - 2).Distinct().Count()}{value[^1]}";

演示:

  string[] tests = new string[] {
    "Money","Yellow", "People"
  };

  var report = string.Join(Environment.NewLine, tests
    .Select(test => $"{test} :: {EncodeWord(test)}"));

  Console.Write(report);

结果:

Money :: M3y
Yellow :: Y3w
People :: P4e

【讨论】:

  • 很好地使用范围运算符,^1。值得一提的是,它需要 C#8。
【解决方案4】:

很多人提出了一些很好的解决方案。我有两种解决方案:一种使用 LINQ,另一种不使用。

LINQ,可能和别人没太大区别

if (str.Length < 3) return str;

var midStr = str.Substring(1, str.Length - 2);
var midCount = midStr.Distinct().Count();
return string.Concat(str[0], midCount, str[str.Length - 1]);

非 LINQ

if (str.Length < 3) return str;
    
var uniqueLetters = new Dictionary<char, int>();
var midStr = str.Substring(1, str.Length - 2);
foreach (var c in midStr)
{
   if (!uniqueLetters.ContainsKey(c))
   {
      uniqueLetters.Add(c, 0);
   }
}
        
var midCount = uniqueLetters.Keys.Count();
return string.Concat(str[0], midCount, str[str.Length - 1]);

我用以下 6 个字符串对此进行了测试:

黄色

紫色


嘿嘿嘿

输出:

LINQ: Y3w, Non-LINQ: Y3w  
LINQ: M3y, Non-LINQ: M3y  
LINQ: P4e, Non-LINQ: P4e  
LINQ: Me, Non-LINQ: Me  
LINQ: Y1u, Non-LINQ: Y1u  
LINQ: H1i, Non-LINQ: H1i

Fiddle

性能方面,我猜它们几乎相同,即使不相同,但我还没有对这两种方法进行任何真正的性能测试。我无法想象他们会有很大的不同,如果有的话。唯一真正的区别是,第二条路线将 Distinct() 扩展为它可能在幕后所做的事情(我还没有查看源代码以确定这是否属实,但这是获得计数的一种非常常见的方法。并且第一条路线当然是更少的代码。

【讨论】:

  • 你也可以使用 hashet。即使在if (hashset.Add(c)) 时计数器变量递增。此外,您应该使用Count 而不是Count()。 ;)
  • @Silvermind 所以我查了这两个,只是为了确定。根据thishashset.Add()dictionary.Add() 之间的差异非常小。 (虽然那篇文章现在已经有 10 年历史了,所以请看它的价值。) 至于 CountCount(),如果该属性存在,后者在幕后调用前者;否则它会遍历列表。所以应该没什么区别。 :) (尽管该属性可能更可取,只是为了避免与铸造相关的意外性能问题!)
  • 关于词典;我知道它非常小,但这不是我建议它的原因。既然您不需要该值,为什么要创建字典似乎有必要的歧义。如果没有必要并且没有增加价值,就会产生歧义。
  • 所以,现在我很惊讶,谢谢,;)。我将深入研究。您是否同意我关于在访问 count 属性时抛出异常的观点。这可能看起来很傻,但是当我将隐藏的集合传递给接受 IEnumerable 的公共方法时,我不希望它会中断。顺便说一句,我在我的手机上,我无法确定这是否是 dotnet 核心或框架的来源。他们可能也会这样做,但我真的很震惊。
  • 随时!而且我大约 90% 确定那是 .NET Core,尽管我认为该特定方法的代码不太可能在两者之间有所不同。这是非常通用的代码。 :) 不过,我也无法验证这一点。至于例外,您可以这样做。我想不出你会想随便的场景,但我想没有什么能阻止你。我认为最后你是对的:Count() 很好,但这是我之前提到的 - 使用 Count 意味着你确切地知道集合是什么,这可能最终更安全。
【解决方案5】:

我会为此目的使用 Linq:

            string[] words = new string[] { "Yellow" , "People", "Money", "Sh" }; // Sh for 2 letter words (or u can insert 0 and then remove the trinary operator)
            foreach (string word in words)
            {
                int uniqeCharsInBetween = word.Substring(1, word.Length - 2).ToCharArray().Distinct().Count();
                string result = word[0] + (uniqeCharsInBetween == 0 ? string.Empty : uniqeCharsInBetween.ToString()) + word[word.Length - 1];
                Console.WriteLine(result);
            }

【讨论】:

    猜你喜欢
    • 2016-11-25
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 2019-09-10
    • 1970-01-01
    • 2017-01-25
    • 2018-07-21
    相关资源
    最近更新 更多