这并没有以任何方式优化性能。我相信您可以使用某种类型的子字符串搜索优化 (Boyer-Moore?) 来提高性能。
一般来说,LINQ 解决方案不会比过程解决方案更有效,尤其是在处理索引速度很快的数组时。
使用几个扩展方法,您可以找到所有常见的子序列。首先,为一个序列生成所有可能的子序列的扩展方法:
public static IEnumerable<IEnumerable<T>> Subsequences<T>(this IEnumerable<T> src) =>
Enumerable.Range(0, src.Count() - 1)
.Select(k => src.Skip(k))
.SelectMany(s1s => Enumerable.Range(2, s1s.Count() - 1).Select(k => s1s.Take(k)));
通过跳过越来越多的开始项目来生成所有序列(长度至少为 2),然后通过从末尾删除越来越多的项目来生成这些序列的所有子序列。
使用两个参数Select方法,你可以记住每个项目的位置以备后用。我转换为List,因此调用Count 时子序列生成效率更高:
var s1ps = s1.Select((n,i) => (n,i)).ToList().Subsequences();
var s2ps = s2.Select((n,i) => (n,i)).ToList().Subsequences();
现在,您可以从源中找到所有匹配的子序列。然后,您可以按照它们在原始源中的起始位置对匹配进行分组,并保留最长的匹配,并按结束位置分组并保留最长的匹配。为了解决这个问题,请使用扩展方法,将 IEnumerable 按键函数分组,然后根据值函数保持最大值:
public static IEnumerable<T> MaximumMatch<T, TKey>(this IEnumerable<T> src, Func<T,TKey> keyFn, Func<T,int> valueFn) =>
src.GroupBy(keyFn).Select(sg => sg.OrderByDescending(s => valueFn(s)).First());
通过两次应用此扩展,您可以获得所有最长的匹配子序列:
var ans = s1ps.SelectMany(as1p => s2ps.Where(as2p => as1p.Count() == as2p.Count()).Where(as2p => as1p.Select(sp => sp.n).SequenceEqual(as2p.Select(sp => sp.n))).Select(as2p => (as1p,as2p)))
.MaximumMatch(st => st.as1p.First().i, st => st.as1p.Count())
.MaximumMatch(st => st.as1p.Last().i, st => st.as1p.Count())
.Select(stg => new { s1begin = stg.as1p.First().i, s1end = stg.as1p.Last().i, s2begin = stg.as2p.First().i, s2end = stg.as2p.Last().i });
最后,您获取每个匹配的子序列并将其投影到其开始和结束位置。