c# - 使用 Linq 在 IEnumerable<T> 中查找序列

标签 c# .net linq

IEnumerable<T> 中查找序列的最有效方法是什么?使用 LINQ

我希望能够创建一个允许以下调用的扩展方法:

int startIndex = largeSequence.FindSequence(subSequence)

匹配必须是相邻的并且是有序的。

最佳答案

下面是一个在序列中查找子序列的算法的实现。我将方法称为 IndexOfSequence,因为它使意图更加明确并且类似于现有的 IndexOf 方法:

public static class ExtensionMethods
{
    public static int IndexOfSequence<T>(this IEnumerable<T> source, IEnumerable<T> sequence)
    {
        return source.IndexOfSequence(sequence, EqualityComparer<T>.Default);
    }

    public static int IndexOfSequence<T>(this IEnumerable<T> source, IEnumerable<T> sequence, IEqualityComparer<T> comparer)
    {
        var seq = sequence.ToArray();

        int p = 0; // current position in source sequence
        int i = 0; // current position in searched sequence
        var prospects = new List<int>(); // list of prospective matches
        foreach (var item in source)
        {
            // Remove bad prospective matches
            prospects.RemoveAll(k => !comparer.Equals(item, seq[p - k]));

            // Is it the start of a prospective match ?
            if (comparer.Equals(item, seq[0]))
            {
                prospects.Add(p);
            }

            // Does current character continues partial match ?
            if (comparer.Equals(item, seq[i]))
            {
                i++;
                // Do we have a complete match ?
                if (i == seq.Length)
                {
                    // Bingo !
                    return p - seq.Length + 1;
                }
            }
            else // Mismatch
            {
                // Do we have prospective matches to fall back to ?
                if (prospects.Count > 0)
                {
                    // Yes, use the first one
                    int k = prospects[0];
                    i = p - k + 1;
                }
                else
                {
                    // No, start from beginning of searched sequence
                    i = 0;
                }
            }
            p++;
        }
        // No match
        return -1;
    }
}

我没有完全测试它,所以它可能仍然包含错误。我只是对众所周知的角落案例进行了一些测试,以确保我没有掉入明显的陷阱。到目前为止似乎工作正常......

我认为复杂度接近于 O(n),但我不是 Big O 符号的专家所以我可能是错的......至少它只枚举源序列一次,而不会返回,所以它应该相当有效。

关于c# - 使用 Linq 在 IEnumerable<T> 中查找序列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3561776/

相关文章:

C#:如何在特定时间启动线程

java - 如何验证端口是否开放

.net - .net 4.0 中即将出现的 'dynamic' 关键字将如何让我的生活更美好?

c# - Linq 获取按最快日期排序的第一行,其中 B 列不是 "Expired"或 "Cancelled"

c# - 如何使用 HtmlAgilityPack 解析 <option> 标签的 InnerText?

c# - 如何创建文件或将文件上传到 Azure Data Lake Storage Gen2

c# - 使用 WCF 和 C# (F#) 收集类似散点的操作

c# - 如何在同一操作中添加范围后删除范围

c# - LINQ 和使用 DataTable 进行分页 - 无法让 Skip 工作?

c# - 在 Visual Studio 2008 中单步执行 C# 时如何找到方法调用方?