在字符串列表中查找公共字符串

时间:2012-11-22 09:17:38

标签: c# linq string-comparison

我非常接近这一点。如果我能看一下这个问题,我昨天就向我提出了一个问题。

我感觉很亲密,但我认为这里的一些人也会很欣赏这个挑战而且我迷失了。

如果我的List<string>有以下成员:

  

今天

     周一

     周二

     

星期三

我想获得一个返回字符串day,因为这是List<string>中的最大公共字符串。无论位置和字符串长度如何,都应该这样做,只想在一大串字符串中找到最大长度的公共字符串。

我的尝试失败了一点,我选择了:

  

周一至周二

     

周一至周三

然后在每个之间做了Intersect。显然这会返回多个字符串,但是对于Monday - Wednesday,您会得到nday,因为这就是它常见的字母。

这是我的代码:

  List<string> strs = new List<string>();
  strs.Add("Monday");
  strs.Add("Tuesday");
  strs.Add("Wednesday");

  var v = strs.SelectMany((day, i) => strs.Select((day2, j) => new
  {
    iDay = i,
    Day = day,
    iDay2 = j,
    Day2 = day2
  })).Where(x => x.iDay != x.iDay2).Select(x => new string(x.Day.Intersect(x.Day2).ToArray()));

任何人都有一个很好的解决方案吗?

注意

它不一定是LINQ

如果没有常用字符串,请返回null或空字符串。

2 个答案:

答案 0 :(得分:7)

这比我的第一种方法(罢工)更好。

您可以使用以下扩展名来获取列表中最短字符串的所有子字符串(以提高效率):

public static IEnumerable<string> getAllSubstrings(this string word)
{
    return from charIndex1 in Enumerable.Range(0, word.Length)
           from charIndex2 in Enumerable.Range(0, word.Length - charIndex1 + 1)
           where charIndex2 > 0
           select word.Substring(charIndex1, charIndex2);
}
  • 现在按Length(最长的第一个)
  • 排序这些子字符串
  • 查看是否所有其他字符串(不包括字符串本身,因为该测试是冗余的)包含子字符串(Enumerable.All如果一个字符串不包含给定的子字符串则立即返回)
  • 如果在所有其他字符串中出现一个字符串,则表示找到了最长的公共子字符串
  • 否则重复这一点,直到您检查完所有子串(如果没有找到常见字符串)

string shortest = list.OrderBy(s => s.Length).First();
IEnumerable<string> shortestSubstrings = shortest
    .getAllSubstrings()
    .OrderByDescending(s => s.Length);
var other = list.Where(s => s != shortest).ToArray();
string longestCommonIntersection = string.Empty;
foreach (string subStr in shortestSubstrings)
{
    bool allContains = other.All(s => s.Contains(subStr));
    if (allContains)
    {
        longestCommonIntersection = subStr;
        break;
    }
}

DEMO

答案 1 :(得分:3)

找到列表中最短的条目。

  • 今天
  • 星期一
  • 星期二
  • 星期三

所以我们使用“今天”。

以“最长的第一个”顺序在每个字符的字符串长度的“今天”中构建一个连续字符串的列表。

“今天”,

“Toda”,“今天”,

“Tod”,“oda”,“day”,

“To”,“od”,“da”,“ay”,

“t”,“o”,“d”,“a”,“y”

枚举此列表,找到所有其他字符串包含该条目的第一个条目。

        List<string> words = new List<string> { "Today", "Monday", "Tuesday", "Wednesday" };

        // Select shortest word in the list
        string shortestWord = (from word in words
                            orderby word.Length
                            select word).First();

        int shortWordLength = shortestWord.Length;

        // Build up the list of consecutive character strings, in length order.
        List<string> parts = new List<string>();
        for (int partLength = shortWordLength; partLength > 0; partLength--)
        {
            for (int partStartIndex = 0; partStartIndex <= shortWordLength - partLength; partStartIndex++)
            {
                parts.Add(shortestWord.Substring(partStartIndex, partLength));
            }
        }
        // Find the first part which is in all the words.
        string longestSubString = (from part in parts where words.All(s => s.Contains(part)) select part).FirstOrDefault();

       // longestSubString is the longest part of all the words, or null if no matches are found.

修改

稍微考虑一下,你可以稍微优化一下。

您无需建立零件清单 - 只需在生成零件时对其进行测试即可。此外,通过按长度顺序对单词列表进行排序,您总是先测试最短的字符串,以便更快地拒绝候选部分。

        string longestSubString = null;

        List<string> words = new List<string> { "Todays", "Monday", "Tuesday" };

        // Sort word list by length
        List<string> wordsInLengthOrder = (from word in words
                                           orderby word.Length
                                           select word).ToList();

        string shortestWord = wordsInLengthOrder[0];
        int shortWordLength = shortestWord.Length;

        // Work through the consecutive character strings, in length order.
        for (int partLength = shortWordLength; (partLength > 0) && (longestSubString == null); partLength--)
        {
            for (int partStartIndex = 0; partStartIndex <= shortWordLength - partLength; partStartIndex++)
            {
                string part = shortestWord.Substring(partStartIndex, partLength);

                // Test if all the words in the sorted list contain the part.
                if (wordsInLengthOrder.All(s => s.Contains(part)))
                {
                    longestSubString = part;
                    break;
                }
            }

        }

        Console.WriteLine(longestSubString);