【问题标题】:Find How many times an Anagram is contained in a String - asp.net c#查找字符串中包含多少次 Anagram - asp.net c#
【发布时间】:2020-03-31 11:30:13
【问题描述】:

我需要像这个例子一样找出一个字符串中包含多少次字谜:(字谜和字符串本身)

输入 1(String) = thegodsanddogsweredogged

输入 2(字符串)= 狗

输出(int) = 3 由于这些原因,输出将是 3 - (thegodsanddogsweredogged)

到目前为止,我设法检查了字符串中包含多少次“狗”这个词:

        public ActionResult FindAnagram (string word1, string word2)
        {
            int ?count = Regex.Matches(word1, word2).Count;

            return View(count);
        }

这可以检查单词包含多少次,但我仍然收到错误:无法将 null 转换为 'int',因为它是不可为空的值类型。

所以我需要检查input 2 的次数和input 2(dog,god,dgo,ogd 等) 的字谜包含在input 1 中吗?(在这种情况下它是 3 次-thesanddogsweredogged)

谢谢

【问题讨论】:

  • 允许重叠吗? “dogdo”是否包含 3 个 dog 字谜(dog、ogd、gdo)?
  • 没有重叠,谢谢!

标签: c# asp.net asp.net-mvc model-view-controller


【解决方案1】:

我想发布一个更易读的变体,但会牺牲一些运行时性能。 Anton 的答案可能更高效,但恕我直言,它的可读性不如预期。

字谜的好处是您知道它们的确切长度,并且您可以很容易地找出所有可能的字谜位置。对于 100 个字母的大海捞针中的 3 个字母字谜,您知道有 98 个可能的位置:

  • 0..2
  • 1..3
  • 2..4
  • ...
  • 96..98
  • 97..99

这些索引可以很容易地生成:

var amountOfPossibleAnagramLocations = haystack.Length - needle.Length + 1;

var substringIndexes = Enumerable.Range(0, amountOfPossibleAnagramLocations);

此时,您只需获取每个列出的子字符串并测试它是否是一个字谜。

var anagramLength = needle.Length;
int count = 0;

foreach(var index in substringIndexes)
{
    var substring = haystack.Substring(index, anagramLength);

    if(substring.IsAnagramOf(needle))
        count++;
}

请注意,其中的很多内容都可以压缩成一个 LINQ 链:

var amountOfPossibleAnagramLocations = haystack.Length - needle.Length + 1;
var anagramLength = needle.Length;

var anagramCount = Enumerable
                      .Range(0, amountOfPossibleAnagramLocations)
                      .Select(x => haystack.Substring(x, anagramLength))
                      .Count(substring => substring.IsAnagramOf(needle));

它是否更具可读性取决于您对 LINQ 的熟悉程度。我个人更喜欢它(当然,要达到合理的尺寸)。


要检查字谜,只需对字符进行排序并检查是否相等。我使用了一种扩展方法来获得可读性奖励:

public static bool IsAnagramOf(this string word1, string word2)
{
    var word1Sorted = String.Concat(word1.OrderBy(c => c));
    var word2Sorted = String.Concat(word2.OrderBy(c => c));

    return word1Sorted == word2Sorted;
}

为了简洁起见,我省略了不区分大小写或忽略空格等内容。

【讨论】:

  • 谢谢你,我会尝试实现这两个答案,我会回复
  • 我应该在控制器中创建一个方法(IsAnagramOf),在 ActionResult 中更具体吗?或者我应该只使用代码?什么是更好的?除此之外,我对您的示例的理解要好得多,对于初学者来说,这似乎要容易得多。非常感谢! :)
  • 最后我把IsAnagramOf 方法放在ActionResult 之外,并将if 修改为if(IsAnagramOf(substring, neddle)) count++; 这种方法似乎可行。
  • @sgcata:如果你把它变成一个字符串扩展方法,它往往会放在一个工具包/帮助器部分,它通常在它自己的文件夹/项目中。但是您也可以将此方法作为“字谜检查器服务”类的一部分(尽管当时不作为扩展方法)。没有关于它应该去哪里的通用规则,请根据您的代码库对其进行适当的分类。但是,您的业务逻辑都不属于控制器,因为控制器有自己的用途(将 Web 请求路由到 您的业务逻辑)
【解决方案2】:

最好不要尝试使用正则表达式,而是编写自己的逻辑。

您可以使用带有键 char 的字典 - 单词的字母和值 int - 字母出现的次数。并为这个词建立这样的字典。

Anagrams 将具有相似的字典,因此您可以为使用 Windowing 方法在您的 str 上构建的每个临时字符串构建一个临时字典,并将其与为您的单词构建的字典进行比较。

这是我的代码:

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {

            var str = "thegodsanddogsweredogged";
            var word = "dog";
            Console.WriteLine("Word: " + word);
            Console.WriteLine("Str: " + str);
            Console.WriteLine();

            var count = CountAnagrams(str, word);
            Console.WriteLine("Count: " + count);

            Console.ReadKey();
        }


        private static int CountAnagrams(string str, string word) {
            var charDict = BuildCharDict(word);
            int count = 0;

            for (int i = 0; i < str.Length - word.Length + 1; i++) {
                string tmp = "";
                for (int j = i; j < str.Length; j++) {
                    tmp += str[j];
                    if (tmp.Length == word.Length)
                        break;
                }

                var tmpCharDict = BuildCharDict(tmp);

                if (CharDictsEqual(charDict, tmpCharDict)) {
                    count++;
                    Console.WriteLine("Anagram: " + tmp);
                    Console.WriteLine("Index: " + i);
                    Console.WriteLine();
                }

            }


            return count;
        }

        private static Dictionary<char, int> BuildCharDict(string word) {
            var charDict = new Dictionary<char, int>();
            foreach (var ch in word)
            {
                if (charDict.ContainsKey(ch))
                {
                    charDict[ch] += 1;
                }
                else
                {
                    charDict[ch] = 1;
                }
            }

            return charDict;
        }

        private static bool CharDictsEqual(Dictionary<char, int> dict1, Dictionary<char, int> dict2) 
        {
            if (dict1.Count != dict2.Count)
                return false;

            foreach (var kv in dict1) {
                if (!dict2.TryGetValue(kv.Key, out var val) || val != kv.Value)
                {
                    return false;
                }
            }

            return true;
        } 
    }
}

可能有更好的解决方案,但我的工作。

附:关于你的错误。您应该将int? 更改为int,因为您的视图可能期望不可为空的int 类型

【讨论】:

  • 我真的很好奇:与对字符串字符进行排序和进行简单的相等性检查相比,生成字典是否有已知的性能提升?
  • @Flater 为我们的解决方案创建基准会很酷,因为这对我来说也很有趣。可以的,有空我会做的。看起来你的代码有很多字符串对象的分配
  • 我认为我的可以优化为只从数组中获取字符而不是创建新字符串,但我试图最大限度地提高可读性(并尽可能远离索引杂耍)而不是尝试找到一个中间立场:)
猜你喜欢
  • 2019-04-01
  • 2017-03-16
  • 2013-02-05
  • 2019-05-21
  • 2012-12-06
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 2017-02-12
相关资源
最近更新 更多