c#提取非重叠排序范围的最有效方法

时间:2017-03-30 10:48:24

标签: c# range disjoint-union

为了突出显示单个字符串中的多个搜索文本元素,我有一个例程计算要在字符串本身中突出显示的范围。

例如,如果我在字符串&#34中搜索his+is字符串;这是我的错误测试"我得到了高亮范围[1,3][2,3][5,6][12,13]

所以我想要的结果是[1,3][5,6][12,13]

是否有一般方法从上面的列表中提取非重叠范围?或事件更好,是否有特定于字符串的方式来获取这些?

2 个答案:

答案 0 :(得分:1)

  1. 按起始索引对范围进行排序。 (你的程序很可能已经这样做了)
  2. 选择第一个范围
  3. 跳过在当前所选范围结束之前开始的所有范围(继续检查下一个范围,直到找到在当前所选范围结束后开始的范围)
  4. 选择新范围
  5. 转到3
  6. 如果你想以文本为基础,这取决于你可能的搜索模式的复杂性(正则表达式?)。如果你指定了这个,我很乐意帮你解决。

答案 1 :(得分:0)

高效你有什么理解?快速?最少的内存使用? Mantainable代码?

有很多方法可以解决这个问题,其中一些方法似乎需要很多代码,但也许不错。例如,请考虑以下方法:

public struct Interval<T> where T: IComparable<T>
{
    public T LowerBound { get; }
    public T UpperBound { get; }

    public Interval(T lowerBound, T upperBound)
    {
        Debug.Assert(upperBound.CompareTo(lowerBound) > 0);
        LowerBound = lowerBound;
        UpperBound = upperBound;
    }

    public static bool AreOverlapping(Interval<T> first, Interval<T> second) => 
        first.UpperBound.CompareTo(second.LowerBound) > 0 &&
        second.UpperBound.CompareTo(first.LowerBound) > 0;

    public static Interval<T> Union(Interval<T> first, Interval<T> second)
    {
        Debug.Assert(AreOverlapping(first, second));
        return new Interval<T>(Min(first.LowerBound, second.LowerBound),
                               Max(first.UpperBound, second.UpperBound));
    }

    public override string ToString() => $"[{LowerBound}, {UpperBound}]";

    private static T Min(T t1, T t2)
    {
        if (t1.CompareTo(t2) <= 0)
            return t1;

        return t2;
    }

    private static T Max(T t1, T t2)
    {
        if (t1.CompareTo(t2) >= 0)
            return t1;

        return t2;
    }
}

现在,我们提取非重叠区间的方法是:

public static IEnumerable<Interval<T>> GetOverlappingIntervals<T>(this IEnumerable<Interval<T>> intervals)
    where T : IComparable<T>
{
    var stack = new Stack<Interval<T>>();

    foreach (var interval in intervals.OrderBy(i => i.LowerBound))
    {
        if (stack.Count == 0)
        {
            stack.Push(interval);
        }
        else
        {
            var previous = stack.Peek();

            if (Interval<T>.AreOverlapping(interval, previous))
            {
                stack.Pop();
                stack.Push(Interval<T>.Union(interval, previous));
            }
            else
            {
                stack.Push(interval);
            }
        }
    }

    return stack;
}

请注意,此解决方案不会执行相邻间隔的并集,不确定这是否是您想要的。

这个解决方案难以理解吗?是的,代码几乎是自我解释的。它效率最高吗?好吧,可能不是,但谁关心它是否“足够”并且它符合你的性能目标。如果没有,那么就开始优化它。