根据一组重叠范围将字符串分成多个范围

时间:2019-06-14 17:42:48

标签: c# string algorithm

我需要根据一组范围值将一个字符串分成多个子字符串,每个子字符串都标记有一组适用范围。结果的子字符串集合中不应有重叠,并且与集合中任何范围都不匹配的子字符串仍需要包含在结果中。

当前,我遍历字符串以找到每个字符的匹配范围,然后循环遍历该迭代的结果以基于连续的匹配范围集构建每个子字符串。但是,我敢肯定,这样做的效率更高。

此应用程序是用C#编写的,但是也可以接受用于解决此问题的算法的文字描述。

示例

让字符串为:

  

selerisquesuspensione congue居民scelerisque sociis placerat a himenaeos diam nunc前庭未成年人nisl himenaeos viverra mus hac。

让范围指定为开始和结束索引(包括开始和结束):

  1. (12,50)-与“ suspendisse congue scelerisque”匹配
  2. (24,29)-匹配“ congue”
  3. (59,88)-匹配“ placerat a himenaeos diam nunc”
  4. (80,103)-匹配“ diam nunc前庭nec”
  5. (114,127)-匹配“ nisl himenaeos”

结果应为:

  1. 范围(0,11),子字符串“ Scelerisque”,匹配的范围[]
  2. 范围(12,23),子字符串“ suspendisse”,匹配的范围[1]
  3. 范围(24,29),子字符串“ congue”,匹配的范围[1,2]
  4. 范围(30,50),子字符串“居民scelerisque”,匹配范围[1]
  5. 范围(51,58),子字符串“ sociis”,匹配的范围[]
  6. 范围(59,79),子字符串“ placerat a himenaeos”,匹配范围[3]
  7. 范围(80,88),子字符串“ diam nunc”,匹配的范围[3,4]
  8. 范围(89,103),子字符串“ vestibulum nec”,匹配范围[4]
  9. 范围(104,113),子字符串“ ultrices”,匹配的范围[]
  10. 范围(114,127),子字符串“ nisl himenaeos”,匹配的范围[5]
  11. 范围(128,144),子字符串“ viverra mus hac。”,匹配范围[]

其他信息

范围永远不会为零长度。分割操作的结果也不应包含任何零长度的子字符串。

除了简单的重叠之外,多个范围可以具有相同的起始索引或结束索引。上面的示例中没有对此进行演示。

2 个答案:

答案 0 :(得分:1)

  • 将范围分成事件(索引,类型,范围):1.(12,50)=>(12,开始,1)和(50,结束,1)
  • 对它们进行排序
  • 初始化一个空的整数集,并且currentIndex = 0
  • 重复每个事件
  • 如果事件是开始,则推入结果(currentIndex,event.index-1),匹配range = set.toArray()。添加event.range进行设置。设置currentIndex = event.index
  • 如果事件结束,则推入结果(currentIndex,event.index),并匹配range = set.toArray()。从集合中删除event.range。设置currentIndex = event.index +1
  • 经过全部迭代后,添加缺少的结果:(currentIndex,string.length),匹配[]

此功能假定事件具有唯一索引。需要进行一些修改以处理[[12,50),(12,40),(15、50)]之类的事情。

答案 1 :(得分:0)

这是我最基本的最坏情况的实现,先用O(m * n)构建rangedChars,然后用O(n log n)将其转换为substrings

我自己编写的代码不是使用包含范围的范围,而是在开始和长度上起作用。

var text = "Scelerisque suspendisse congue habitant scelerisque sociis placerat a himenaeos diam nunc vestibulum nec ultrices nisl himenaeos viverra mus hac.";
var ranges = new Range[]
{
    new Range(12, 39),
    new Range(24, 6),
    new Range(59, 30),
    new Range(80, 24),
    new Range(114, 14)
};

var rangedChars = new RangedChar[text.Length];
for (int i = 0; i < text.Length; i++)
    rangedChars[i] = new RangedChar(i, text[i], ranges.Where(r => r.ContainsIndex(i)));

var substrings = new List<RangedString>();
for (int i = 0; i < rangedChars.Length; )
{
    var appliedRanges = rangedChars[i].Ranges;
    int j = i + 1;
    while (j < rangedChars.Length && appliedRanges.SequenceEqual(rangedChars[j].Ranges))
        j++;

    var substring = String.Join("", text.Substring(i, j - i));
    substrings.Add(new RangedString(i, substring, appliedRanges));

    i = j;
}

return substrings;

// supporting types

readonly struct Range
{
    public int Start { get; }
    public int Length { get; }
    public Range(int start, int end) { Start = start; End = end; }
    public bool ContainsIndex(int index) => index >= Start && index < (Start + Length);
}

public class RangedChar
{
    public int Index { get; }
    public char Character { get; }
    public IReadOnlyList<Range> Ranges { get; }
    public RangedChar(int index, char character, IEnumerable<Range> ranges)
    {
        Index = index;
        Character = character;
        Ranges = ranges.ToList();
    }
}

public class RangedString
{
    public int Start { get; }
    public string Text { get; }
    public IReadOnlyList<Range> Ranges { get; }
    public RangedString(int start, string text, IEnumerable<Range> ranges)
    {
        Start = start;
        Text = text;
        Ranges = ranges.ToList();
    }
}