我想在列表B中找到一个通用的点NET列表A - 如何在列表B中找到列表A?我需要列表A在列表B中开始的索引。
答案 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)
尝试接收ListA
和ListB
:
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