linq订购了另一个列表的子集

时间:2015-07-21 08:42:47

标签: c# linq set

关于查找一个列表是否是另一个列表的子集,有很多很多问题。

即。 bool isSubset = !t2.Except(t1).Any();

我似乎无法找到一个说明订单

如给定序列:

1,1,2,5,8,1,9,1,2

子序列......

2,5,8,1,9 true

1,2,5,8,1 true

5,2,1 false

1,2,5,1,8 false

1,1,2 true

1,1,1,2 false

6 个答案:

答案 0 :(得分:4)

订单重要的列表是 string 概念的概括。因此,您希望使用子字符串查找算法。

有几种可能性,但Knuth-Morris-Pratt是个不错的选择。它有一些初始Θ(m)开销,其中m是所寻找的子列表的长度,然后在Θ(n)中找到,其中n是到所寻找的子列表的距离,或者如果它不在那里则是整个列表的长度。这击败了简单的逐项比较,即Θ((n-m + 1)m):

public static class ListSearching
{
  public static bool Contains<T>(this IList<T> haystack, IList<T> needle)
  {
    return Contains(haystack, needle, null);
  }
  public static bool Contains<T>(this IList<T> haystack, IList<T> needle, IEqualityComparer<T> cmp)
  {
    return haystack.IndexOf(needle, cmp) != -1;
  }
  public static int IndexOf<T>(this IList<T> haystack, IList<T> needle)
  {
    return IndexOf(haystack, needle, null);
  }
  public static int IndexOf<T>(this IList<T> haystack, IList<T> needle, IEqualityComparer<T> cmp)
  {
    if(haystack == null || needle == null)
      throw new ArgumentNullException();
    int needleCount = needle.Count;
    if(needleCount == 0)
      return 0;//empty lists are everywhere!
    if(cmp == null)
      cmp = EqualityComparer<T>.Default;
    int count = haystack.Count;
    if(needleCount == 1)//can't beat just spinning through for it
    {
      T item = needle[0];
      for(int idx = 0; idx != count; ++idx)
        if(cmp.Equals(haystack[idx], item))
          return idx;
      return -1;
    }
    int m = 0;
    int i = 0;
    int[] table = KMPTable(needle, cmp);
    while(m + i < count)
    {
      if(cmp.Equals(needle[i], haystack[m + i]))
      {
        if(i == needleCount - 1)
          return m == needleCount ? -1 : m;//match -1 = failure to find conventional in .NET
        ++i;
      }
      else
      {
        m = m + i - table[i];
        i = table[i] > -1 ? table[i] : 0;
      }
    }
    return -1;
  }
  private static int[] KMPTable<T>(IList<T> sought, IEqualityComparer<T> cmp)
  {
    int[] table = new int[sought.Count];
    int pos = 2;
    int cnd = 0;
    table[0] = -1;
    table[1] = 0;
    while(pos < table.Length)
      if(cmp.Equals(sought[pos - 1], sought[cnd]))
        table[pos++] = ++cnd;
      else if(cnd > 0)
        cnd = table[cnd];
      else
        table[pos++] = 0;
    return table;
  }
}

测试:

var list = new[]{ 1, 1, 2, 5, 8, 1, 9, 1, 2 };
Console.WriteLine(list.Contains(new[]{2,5,8,1,9})); // True
Console.WriteLine(list.Contains(new[]{1,2,5,8,1})); // True
Console.WriteLine(list.Contains(new[]{5,2,1}));     // False
Console.WriteLine(list.Contains(new[]{1,2,5,1,8})); // False
Console.WriteLine(list.Contains(new[]{1,1,2}));     // True
Console.WriteLine(list.Contains(new[]{1,1,1,2}));   // False

答案 1 :(得分:2)

不幸的是.net中没有这样的功能。你需要Knuth-Morris-Pratt算法。一个人已将其实现为linq extension https://code.google.com/p/linq-extensions/

答案 2 :(得分:2)

这对我有用:

var source = new [] { 1,1,2,5,8,1,9,1,2 };

Func<int[], int[], bool> contains =
    (xs, ys) =>
        Enumerable
            .Range(0, xs.Length)
            .Where(n => xs.Skip(n).Take(ys.Length).SequenceEqual(ys))
            .Any();

Console.WriteLine(contains(source, new [] { 2,5,8,1,9 })); // true
Console.WriteLine(contains(source, new [] { 1,2,5,8,1 })); // true
Console.WriteLine(contains(source, new [] { 5,2,1 })); // false
Console.WriteLine(contains(source, new [] { 1,2,5,1,8 })); // false
Console.WriteLine(contains(source, new [] { 1,1,2 })); // true
Console.WriteLine(contains(source, new [] { 1,1,1,2 })); // false

答案 3 :(得分:0)

有限制的解决方法。您可以将可枚举更改为字符串,然后使用Contains方法。

 var t1 = new List<int> {1, 1, 2, 5, 8, 1, 9, 1, 2};
         var t2 = new List<int> {2,5,8,1,9};
         var t3 = new List<int> {5,2,1};

         var t1Str = String.Join(",", t1);

t1Str.Contains(String.Join(",", t2););//true
t1Str.Contains(String.Join(",", t3););//false

答案 4 :(得分:0)

你可以构建自己的扩展,我写了一个简单的IsSubset方法:

用于测试的控制台应用

public static class IEnumerableExtensions
{
    public static bool IsSubset<T>(this IEnumerable<T> subsetEnumerable, IEnumerable<T> enumerable)
    {
        var found = false;

        var list = enumerable as IList<T> ?? enumerable.ToList();
        var listCount = list.Count();

        var subsetList = subsetEnumerable as IList<T> ?? subsetEnumerable.ToList();
        var posListCount = subsetList.Count();

        /* If the SubList is bigger, it can't be a sublist */
        if (listCount < posListCount) { 
            return false;
        }

        /* find all indexes of the first item of the sublist in the list */
        var firstElement = subsetList.First();
        var indexes = new List<int>();
        var index = 0;
        foreach (var elem in list)
        {
            if (elem.Equals(firstElement))
            {
                indexes.Add(index);
            }
            index++;
        }

        /* check all first item founds for the subsequence */
        foreach (var i in indexes)
        {
            int x=0;
            for (x = 0; x < posListCount && (i + x) < listCount; x++)
            {
                if (!Equals(subsetList[x], list[(i + x)]))
                {
                    found = false;
                    break;
                }
                found = true;
            }

            if (x + 1 < posListCount)
                found = false;
        }

        return found;
    }
}

IEnumerable Extension:

SELECT max(t),a,b,c,id FROM table GROUP BY A,B,C,id ORDER BY ID, max(T)

答案 5 :(得分:0)

可能正在使用加入可以得到你想要的。 Join将返回匹配的记录。如果记录计数大于0,则匹配,否则不匹配。

下面我通过示例代码进行了解释:

class Program
{
    static void Main(string[] args)
    {
        List<Employee> empList = new List<Employee> 
        {
            new Employee{EmpID = 1},
            new Employee{EmpID = 1},
            new Employee{EmpID = 2},
            new Employee{EmpID = 5},
            new Employee{EmpID = 8},
            new Employee{EmpID = 1},
            new Employee{EmpID = 9},
            new Employee{EmpID = 1},
            new Employee{EmpID = 2}
        };

        List<Manager> mgrList = new List<Manager> 
        {
            new Manager{ManagerID = 7},
            new Manager{ManagerID = 3},
            new Manager{ManagerID = 6}               
        };

        var result = (from emp in empList
                     join mgr in mgrList on emp.EmpID equals mgr.ManagerID
                     select new { emp.EmpID}).Count();

        Console.WriteLine(result);
        Console.ReadKey();
    }
}

public class Employee
{ 
    public int EmpID { get; set; } 
}

public class Manager
{ 
    public int ManagerID { get; set; }
}