用于检测和替换列表中的组的算法

时间:2012-05-31 15:04:40

标签: c# algorithm grouping

我有一个对象列表: 例如A, A, B, C, D, E, E

我已经定义了模板,告诉我们如何对对象类型进行分组 例如

 Group Alpha --> A 1..n --> any number of 'A's can be grouped
 Group Charlie --> Sequences of 'BCD' can be grouped
 Group Epsilon --> E 1..n --> any number of 'E's can be grouped

现在我想在原始列表中应用这些组定义,这应该给出结果:

 Group Alpha (2x'A'), Group Charlie (1x'BCD'), Group Epsilon (2x'E')

如何才能最好地实现这一目标?我的问题是否有已知的搜索算法/模式? 我尝试了一种非常基本的方法,在列表上循环多次,并试图从每个列表条目和匹配模式中向前看,但由于复杂性而完全丢失...

提前感谢任何暗示!!!

4 个答案:

答案 0 :(得分:2)

这是一个修改过的字符串匹配问题。您有两种类型的输入:

  1. 像“BCD”这样的人。如果您只有这个,可以使用任何传统算法here

  2. 进行匹配
  3. 一行中相同对象的任意数量。

  4. 我脑子里有两种解决方案:

    1. 使用传统的字符串算法(KMP或其他),但为第二种输入类型制定例外规则。

    2. 建立如:

    3. 的有向图

      enter image description here

      上面的数字很糟糕。如果您有任何问题,请告诉我。

答案 1 :(得分:2)

我不完全确定这是你需要的,但是用这个小代码我可以创建你指定的输出

简单用法(带断言):

var a1 = new List<string> { "A", "A", "B", "C", "D", "E", "E" };

a1.ApplyCriteria("A").Criteria.Should().Be("A");
a1.ApplyCriteria("A").Count.Should().Be(2);

a1.ApplyCriteria("E").Criteria.Should().Be("E");
a1.ApplyCriteria("E").Count.Should().Be(2);

a1.ApplyCriteria("BCD").Criteria.Should().Be("BCD");
a1.ApplyCriteria("BCD").Count.Should().Be(1);

a1.ApplyCriteria("CD").Criteria.Should().Be("CD");
a1.ApplyCriteria("CD").Count.Should().Be(1);

// not found
a1.ApplyCriteria("CDA").Criteria.Should().Be("CDA");
a1.ApplyCriteria("CDA").Count.Should().Be(0);

ApplyCriteria方法返回的GroupResult类如下所示:

class GroupResult
{
    public string Criteria { get; set; }
    public int Count { get; set; }
}

这些是正在进行实际工作的扩展方法

static class Ext
{
    public static GroupResult ApplyCriteria(this IEnumerable<string> source, string criteria)
    {
        var elements = source.ToConcatenedString();

        return new GroupResult { Criteria = criteria, Count = elements.CountOcurrences(criteria) };
    }

    public static int CountOcurrences(this string source, string phrase)
    {
        return source
            .Select((c, i) => source.Substring(i))
            .Count(sub => sub.StartsWith(phrase));
    }

    public static string ToConcatenedString<TSource>(this IEnumerable<TSource> source)
    {
        var sb = new StringBuilder();

        foreach (var value in source)
        {
            sb.Append(value);
        }

        return sb.ToString();
    }
}

答案 2 :(得分:1)

假设您有某种代码可以在对象之间进行比较,并告诉A和什么是B,您可以将模板定义为数组,然后遍历原始列表,搜索模板&#39; s出现次数。

CustomObj[] template = new CustomObj[]{B,C,D};
for (int i=0; i< originalList.Length- template.Length + 1; i++)
{
     bool found= true;
     for(int j=0; j< template.Length;j++)
     {
        found = template[j] == originalList[i +j];
     }
     if (found)
     {
        //add to results list
      }
}

搜索比较算法(其中最简单的,据我记得)使用这些概念,还有一些压缩算法,但它们从另一方面开始工作(构建模板以通过模板的creatinbg索引减少存储)

修改
结果我实际上实现了simple Rabin-Karp algorithm
我记得它是这样的:)

答案 3 :(得分:1)

在基础知识中,您可以构建state machine。它将有6个状态,“Init”,“alpha”,“B”,“C”,“charlie”和“epsilon”。

从init开始:

  • 如果下一个对象是“A”,请转到状态alpha,将alpha计数器增加1。
  • 如果下一个obj是B,请转到状态B.
  • 如果下一个对象是“E”,请转到epsilon状态,递增Epsilon计数器。
  • 如果有其他任何对象,请保持初始状态。

在州aplha:

  • 如果下一个对象是A,则保持状态alpha。
  • 如果下一个对象是B,请转到状态B
  • 如果下一个obj是E,请转到epsilon并增加epsilon counter。
  • 如果有的话,请转到init。

在州B:

  • 如果下一个是A,请转到alpha和inc counter
  • 如果下一个是E,请转到epsilon,包括其计数器。
  • 如果next是C,请转到州C
  • 其他 - &gt;转到init

在州C:

  • 如果下一个是A,请转到alpha和inc counter
  • 如果下一个是E,请转到epsilon,包括其计数器。
  • 如果下一个是D,请转到州查理,增加查理计数器
  • 其他 - &gt;转到init

在州D:

  • 如果下一个是A,请转到alpha和inc counter
  • 如果下一个是E,请转到epsilon,包括其计数器。
  • 如果下一个是B,请转到状态B
  • 其他 - &gt;转到init

在州epsilon:

  • 如果下一个对象是“A”,请转到状态alpha,将alpha计数器增加1。
  • 如果下一个obj是B,请转到状态B.
  • 如果下一个对象是“E”,则不执行任何操作。
  • 如果有其他任何对象,请转到初始状态。

我知道它看起来很复杂,但实际上并非如此,至少在这一点上,特别是如果您创建状态图。当然,如果你想要更通用的东西,或者你想继续添加新的模式,或者你有更多的模式,它会很快变得非常复杂。在这种情况下,我认为你的最佳镜头是string matching algorithms中的一个适应你的问题。