比较字符串是否相等

3
我希望能够比较一个字符串集合,并返回相等的部分,直到出现不相等的部分(并删除尾随空格)。
例如:
List<string> strList = new List<string>
{
    "string xyz stop",
    "string abc stop",
    "string   qrt stop"
};

string result = GetEqualName(strList); // This should return "string"

我制作了以下有效的方法。
string GetEqualName(IEnumerable<string> strList)
{
    string outString = "";
    bool firstTime = true;
    foreach (var subString in strList)
    {
        if (firstTime)
        {
            outString = subString;
            firstTime = false;
        }
        else
        {
            string stringBuilder = "";
            for (int i = 0; i < outString.Count(); i++)
            {
                if (outString[i] == subString[i])
                    stringBuilder = stringBuilder + outString[i];
                else
                    break;
            }
            outString = stringBuilder;
        }
    }
    outString = outString.TrimEnd(' '); // Remove traling whitespace
    return outString;
}

我觉得这是可以用几行代码完成的,但我可能在过度设计。你们有什么建议吗?


3
你的示例似乎在比较三个字符串,而不是你所说的两个。 - Jonathan Wood
2
我不知道是否有任何内置函数可以将其缩减为几行代码(除了使用 Linq 聚合的“巧妙”方法)。如果您的方法可行(并且您理解它为什么可行),那么我认为没有必要改变它。 - D Stanley
“subString”可能不是正确的词语。它是集合中的字符串之一。 - Daltons
@JonathanWood 最好将事情变得通用 :) - Daltons
所以你想要找到从左到右列表中所有字符串的最长公共子串?但是,如果它以一个公共子串结尾,那么它应该被忽略。 - Tim Schmelter
4个回答

6
你可以将两个字符串使用Zip函数合并,取出相等的字符对,并将这些字符组成一个新的字符串。
public static string LargestCommonPrefix(string first, string second)
{
    return new string(first.Zip(second, Tuple.Create)
        .TakeWhile(pair => pair.Item1 == pair.Item2)
        .Select(pair => pair.Item1)
        .ToArray());
}

在解决了合并两个字符串的问题之后,您可以轻松地将其应用于一系列字符串:

public static string LargestCommonPrefix(IEnumerable<string> strings)
{
    return strings.Aggregate(LargestCommonPrefix);
}

1
很好地运用了聚合函数来解释“先处理两个情况,然后再扩展”的概念。 - Mark Peters

2
这个小函数基本上和你的版本一样,但更短。
string GetEqualName(IEnumerable<string> strList)
{
    int limit = strList.Min(s => s.Length);

    int i = 0;
    for (; i < limit; i++)
    {
        if (strList.Select(s => s.Substring(0,i+1)).Distinct().Count() > 1)
        {
            break;
        }
    }
    return strList.First().Substring(0, i).Trim();
}

请注意,这种方法效率极低。您一遍又一遍地重复相同的工作,而且由于此处没有短路机制,因此您会计算所有长度小于或等于最终值的所有字符串的所有子字符串。 - Servy
我从未说过它更快 ;) 但说真的,除非字符串变得很大,否则差异是相当微小的。我实际上正在开发一个更快的版本,但是a)这是一个快速发布的答案,是在工作中等待构建时编写的,b)当问问题者的名字是“我对此仍然很新”,而问题是关于像比较字符串这样简单(至少表面上是)的事情时,我想一个易于理解的版本可能会受到赞赏。高效的版本将使用.TakeWhile()或条件zip。此外,它击败了StringBuilder - anaximander

1
这里有一种不同的方法可以实现你想要的功能。它使用一个 HashSet<string> 从左到右查找最长公共子串:
string GetCommonStartsWith(IEnumerable<string> strList, StringComparer comparer = null)
{
    if(!strList.Any() || strList.Any(str => string.IsNullOrEmpty(str)))
        return null;
    if(!strList.Skip(1).Any())
        return strList.First(); // only one

    if(comparer == null) comparer = StringComparer.CurrentCulture;
    int commonLength = strList.Min(str => str.Length);

    for (int length = commonLength; length > 0; length--)
    {
        HashSet<string> duptester = new HashSet<string>(comparer);
        string first = strList.First().Substring(0, length).TrimEnd();
        duptester.Add(first);
        bool allEqual = strList.Skip(1)
            .All(str => !duptester.Add(str.Substring(0, length).TrimEnd()));
        if (allEqual)
            return first;
    }
    return null;
}

1
这是一个使用LINQ较少的版本,可能更具性能优势。
string GetEqualName(IEnumerable<string> strList)
{
    StringBuilder builder = new StringBuilder();
    int minLength = strList.Min(s => s.Length);

    for (int i = 0; i < minLength; i++)
    {
        char? c = null;
        foreach (var s in strList)
        {
            if (c == null)
                c = s[i];
            else if (s[i] != c)
                return builder.ToString().TrimEnd();
        }
        builder.Append(c);
    }
    return builder.ToString().TrimEnd();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接