查找列表B中的列表A?

时间:2013-06-18 01:27:04

标签: .net vb.net linq

我想在列表B中找到一个通用的点NET列表A - 如何在列表B中找到列表A?我需要列表A在列表B中开始的索引。

4 个答案:

答案 0 :(得分:2)

有一种非常天真的方法O(|A| * |B|)。基本上,这是天真的子串搜索算法 1

for(int i = 0; i < B.Count - A.Count; i++) {
     bool matchPossible = true;
     for(int j = 0; matchPossible && j < A.Count; j++) {
         if (!B[i + j].Equals(A[j])) { // should check for B[i + j] == null
              matchPossible = false;
              break;
         }
     }
     if(matchPossible) {
         return i;
     }
 }     
 return -1;

我遗漏了一些明显的错误检查,你应该这样做,你可以专注于这种方法。我评论了一个明显的检查。这将为您提供B中可以找到A的索引。

我坚持这一点,除非基准测试显示这种方法拖累了你。如果是的话,你需要看看像Knuth-Morris-Pratt这样更复杂的东西。

  

列表A和列表B都声明为List<string>;我以为可能有办法用LINQ做到这一点?在列表A中查找列表B

不确定。

for(int i = 0; i < B.Count - A.Count; i++) {
    if(B.SequenceEqual(A.Skip(i).Take(B.Count))) {
        return i;
    }
}
return -1;

请注意,这与我上面给出的算法基本相同,只是表达得更清晰一点。但是,这种方法存在缺点。我不知道Enumerable.Skip是否足够聪明,可以在索引器可用时使用它。如果不使用索引器,则此版本的性能可能低于原始版本。这就是我在最初的方法中没有使用它的原因。

另外,你将不得不翻译成VB.NET,对不起;我没有方便的编译器,我说流利的C#(所以不需要编译器来检查我的语法),但不是VB。

1 :出于某种原因,我有这种突然的回忆,这种方法被K&amp; R的小C书所涵盖?谁能验证;我不知道我的副本现在在哪里? 2

2 :找到它。是的,第4.1节。功能是strindex

答案 1 :(得分:2)

以下是this answer的通用实现,允许任何IList并且可以传入可选的IEqualityComparer。这不是最快的搜索方式,但它是一个很好的起点,可以做出更高级的答案。

static class GenericSearcher
{

    static readonly int[] Empty = new int[0];

    public static int[] Locate<T>(this IList<T> self, IList<T> candidate)
    {
        return Locate(self, candidate, EqualityComparer<T>.Default);
    }

    public static int[] Locate<T>(this IList<T> self, IList<T> candidate, IEqualityComparer<T> comparer)
    {
        if (IsEmptyLocate(self, candidate))
            return Empty;

        var list = new List<int>();

        for (int i = 0; i < self.Count; i++)
        {
            if (!IsMatch(self, i, candidate, comparer))
                continue;

            list.Add(i);
        }

        return list.Count == 0 ? Empty : list.ToArray();
    }

    static bool IsMatch<T>(IList<T> array, int position, IList<T> candidate, IEqualityComparer<T> comparer)
    {
        if (candidate.Count > (array.Count - position))
            return false;

        for (int i = 0; i < candidate.Count; i++)
            if (comparer.Equals(array[position + i],candidate[i]) == false)
                return false;

        return true;
    }

    static bool IsEmptyLocate<T>(ICollection<T> array, ICollection<T> candidate)
    {
        return array == null
            || candidate == null
            || array.Count == 0
            || candidate.Count == 0
            || candidate.Count > array.Count;
    }

}

使用VB代码更新(感谢ajakblackgoat

Module GenericSearcher

    Private ReadOnly Empty As Integer() = New Integer(-1) {}

    <System.Runtime.CompilerServices.Extension> _
    Public Function Locate(Of T)(self As IList(Of T), candidate As IList(Of T)) As Integer()
        Return Locate(self, candidate, EqualityComparer(Of T).[Default])
    End Function

    <System.Runtime.CompilerServices.Extension> _
    Public Function Locate(Of T)(self As IList(Of T), candidate As IList(Of T), comparer As IEqualityComparer(Of T)) As Integer()
        If IsEmptyLocate(self, candidate) Then
            Return Empty
        End If

        Dim list = New List(Of Integer)()

        For i As Integer = 0 To self.Count - 1
            If Not IsMatch(self, i, candidate, comparer) Then
                Continue For
            End If

            list.Add(i)
        Next

        Return If(list.Count = 0, Empty, list.ToArray())
    End Function

    Private Function IsMatch(Of T)(array As IList(Of T), position As Integer, candidate As IList(Of T), comparer As IEqualityComparer(Of T)) As Boolean
        If candidate.Count > (array.Count - position) Then
            Return False
        End If

        For i As Integer = 0 To candidate.Count - 1
            If Not comparer.Equals(array(position + i), candidate(i)) Then
                Return False
            End If
        Next

        Return True
    End Function

    Private Function IsEmptyLocate(Of T)(array As ICollection(Of T), candidate As ICollection(Of T)) As Boolean
        Return array Is Nothing OrElse candidate Is Nothing OrElse array.Count = 0 OrElse candidate.Count = 0 OrElse candidate.Count > array.Count
    End Function

End Module

答案 2 :(得分:1)

尝试接收ListAListB

的此功能
Dim index As Integer = listB.IndexOf(listA(0))
Dim iCont As Integer
Dim bMatch As Boolean

While index >= 0
    bMatch = True
    iCont = 1

    'Check if lists match on all the items in list A
    While bMatch
        index += 1
        bMatch = (index < listB.Count) AndAlso (listA(iCont) = listB(index))

        iCont += 1
        If iCont >= ListA.Count Then Exit While
    End While         

    If bMatch Then Exit While
    index = listB.IndexOf(listA(0), index)
End While

return bMatch

答案 3 :(得分:0)

这是一个相当简单的方法。它适用于任何具有相等比较器的类型。我用字符串和整数列表测试了它。此函数仅按ListB的第一个元素进行迭代。因此,它只会像ListA中的元素一样多次,它们等于ListB中的第一个元素,直到启动相等序列的元素。

Private Function FindList(Of T)(ListA As IList(Of T), ListB As List(Of T)) As Integer
    Dim FoundIndex As Integer = ListA.IndexOf(ListB(0))
    Dim FoundlList As Boolean = False
    While FoundIndex <> -1
        If ListA.Skip(FoundIndex).Take(ListB.Count).SequenceEqual(ListB) Then
            Return FoundIndex
        End If
        FoundIndex = FindIndex(ListA, FoundIndex, ListB(0))
    End While
    Return FoundIndex
End Function
Private Function FindIndex(Of T)(List As IList(Of T), Start As Integer, FindItem As T) As Integer
    Dim TempList = List.Skip(Start + 1).ToList
    Return TempList.IndexOf(FindItem) + (List.Count - TempList.Count)
End Function