检查列表是否连续出现在任何给定列表中

Check if a list appear consecutively in any of the given lists

提问人:Yidi 提问时间:10/20/2023 最后编辑:Dmitry BychenkoYidi 更新时间:10/20/2023 访问量:54

问:

我有一个主要列表:

public List<List<string>> validOrders = new List<List<string>>
{
    new List<string> { "01", "02", "03", "04", "05", "06" },
    new List<string> { "02", "03", "01", "04", "05", "06" },
    new List<string> { "02", "03", "04", "01", "05", "06" },
};

然后我还有另一个列表:

public List<string> childObjectsPrefix = new List<string>();

现在,我想将childObjectsPrefix与validOrders列表进行比较,并检查起始集是否连续出现在任何validOrders列表中。

假设我的用例是这样的:

List<string> test1 = new List<string> { "01", "04" }; // False
List<string> test2 = new List<string> { "01", "02" }; // True
List<string> test3 = new List<string> { "02", "01" }; // False
List<string> test4 = new List<string> { "02", "03", "01" }; // True
List<string> test5 = new List<string> { "02", "03", "05" }; // False

我知道如何检查列表的子集,这相当简单,但这有点棘手。

C# 列表 unity-game-engine

评论

0赞 JonasH 10/20/2023
你能把所有的字符串连接起来做Strings.Contains吗?
0赞 Yidi 10/20/2023
Strings.Contains 将输出 true,因为 01 和 04 都在列表中,但是我想检查它们是否连续出现。
1赞 Matthew Watson 10/20/2023
可能重复 stackoverflow.com/questions/7324177/...
0赞 Yidi 10/20/2023
@MatthewWatson不是真的,因为这里我有 { “01”, “04” },并且在 validOrders[1] 中它们连续出现,但它忽略了前面的值。我正在检查 childObjectsPrefix[0] 和 [1]
0赞 JonasH 10/20/2023
"020301040506".Contains("0104")= true,和 = false,正如你所期望的那样,对吧?Ofc,只有当您对字符串可能包含的内容有一些先验知识时,这种方法才有可能。"010203040506".Contains("0104")

答:

2赞 Den 10/20/2023 #1

您可以连续执行此操作:

public static bool IsConsecutiveSubset(List<List<string>> validOrders, List<string> childObjectsPrefix)
{
    foreach (var validOrder in validOrders)
    {
        if (validOrder.Count < childObjectsPrefix.Count)
            continue; // Skip if the sublist is shorter than childObjectsPrefix

        for (int i = 0; i <= validOrder.Count - childObjectsPrefix.Count; i++)
        {
            if (childObjectsPrefix.SequenceEqual(validOrder.GetRange(i, childObjectsPrefix.Count)))
                return true; // Found a match
        }
    }

    return false; // No consecutive subset found
}

开始代码如下:

public static bool StartsWithPrefix(List<List<string>> validOrders, List<string> childObjectsPrefix)
{
    foreach (var validOrder in validOrders)
    {
        if (validOrder.Count < childObjectsPrefix.Count)
            continue; // Skip if the sublist is shorter than childObjectsPrefix

        if (validOrder.Take(childObjectsPrefix.Count).SequenceEqual(childObjectsPrefix))
            return true; // Found a match
    }

    return false; // No valid start found
}

评论

0赞 Yidi 10/20/2023
感谢您的回答,但不幸的是它输出 { “01”, “04” } 为 true。这是错误的,因为列表不是以这些字符串开头的。
0赞 Den 10/20/2023
开始不是连续的......然后我编辑响应
0赞 Yidi 10/20/2023
是的,对不起,我的意思是从开始。
0赞 Yidi 10/20/2023
谢谢你,这奏效了!:)感谢您的快速解决方案。
1赞 Dmitry Bychenko 10/20/2023 #2

一般情况下任意类型,集合),我们可以使用 Knuth Morris Pratt 算法,请注意,如果列表中的项目被视为“字母” 我们在“strings”中寻找一个“substring”。

即我们可以构建 KMP 数组:

private static int[] KmpArray<T>(IEnumerable<T> sequence, 
                                 IEqualityComparer<T> comparer = default) {
  if (sequence is null)
    throw new ArgumentNullException(nameof(sequence));

  comparer ??= EqualityComparer<T>.Default;

  IReadOnlyList<T> pattern = sequence as IReadOnlyList<T> ?? sequence.ToList();

  int[] result = new int[pattern.Count];

  int length = 0;
  int current = 1;

  while (current < pattern.Count)
    if (comparer.Equals(pattern[current], pattern[length]))
      result[current++] = ++length;
    else if (length != 0)
      length = result[length - 1];
    else
      result[current++] = length;

   return result;
}

然后使用它

public static IEnumerable<int> IndexesOf<T>(IEnumerable<T> source,
                                            IEnumerable<T> pattern,
                                            IEqualityComparer<T> comparer = default) {
  if (source is null)
    throw new ArgumentNullException(nameof(source));
  if (pattern is null)
    throw new ArgumentNullException(nameof(pattern));

  IReadOnlyList<T> pat = pattern as IReadOnlyList<T> ?? pattern.ToList();

  if (pat.Count == 0)
    yield break;

  int[] lps = KmpArray(pat);

  comparer ??= EqualityComparer<T>.Default;

  int i = 0;
  int j = 0;

  using var en = source.GetEnumerator();

  for (bool agenda = en.MoveNext(); agenda;) {
    if (comparer.Equals(pat[j], en.Current)) {
      i += 1;
      j += 1;

      agenda = en.MoveNext();
    }

    if (j == pat.Count) {
      yield return i - j;

      j = lps[j - 1];
    }
    else if (agenda && !comparer.Equals(pat[j], en.Current)) {
      if (j != 0)
        j = lps[j - 1];
      else {
        i += 1;

        agenda = en.MoveNext();
      }
    }
  }
}

完成这些后,我们应该放一个简单的 Linq:

var order = new List<string> { "01", "02" };

bool result = validOrders.Any(item => IndexesOf(item, order).Any());