HOME

Given a list of periods (begin-end) this algorithm will join and merge periods that have intersection.



#include <vector>
#include <algorithm>
#include <cassert>

using namespace std;


typedef int DateTime;

struct Period
{
  DateTime begin = 0;
  DateTime end = 0;
  bool operator == (const Period& other) const
  {
    return begin == other.begin && end == other.end;
  }
  bool operator < (const Period& other) const
  {
    //only begin is used on purpouse
    return begin < other.begin;
  }
};

// This algorithm build a new version of v 
// adding a new period
// v is always in a final state (no orverlapping)
void AddPeriod(vector<Period>& v, Period period)
{
  if (v.empty())
  {
    v.push_back(period);
  }
  else
  {
    //it points to the period that has begin >= period.begin
    auto it = lower_bound(v.begin(), v.end(), period);
    const int index = it == v.end() ? -1 : it - v.begin();

    if (it == v.end())
    {
      //The added period  is represented as { } 
      // 1 - ... [ { ] }
      // 2 - ... [ { } ] 
      // 3 - ... [  ] { }

      if (period.begin < v.back().end)
      {
        // 1 - ... [ { ] }
        // 2 - ... [ { } ] 

        if (period.end > v.back().end)
        {
          // 1 - ... [ { ] }
          v.back().end = period.end; //ok
        }
        else
        {
          // 2 - ... [ { } ]  or exactly the same
        }
      }
      else
      {
        // 3 - ... [  ] { }
        v.push_back(period); //ok
      }
    }
    else
    {
      if (index > 0 && v[index - 1].end > period.begin)
      {
        //  ..[ { ] [ ] [ ] ...
        //          ^            

        it = v.begin() + (index - 1);

        // it now points to the previous period

        //  ..[ { ] [ ] [ ] ...
        //    ^            
      }
      else
      {
        //  ... [ ] { [ ] [ ] ...
        //            ^          

        //fixing the current period begin
        it->begin = period.begin;
      }

      //finding the last period and remove
      //periods in between

      DateTime last = period.end > it->end ? period.end : it->end;

      auto it2 = it + 1;
      for (; it2 != v.end() && it2->begin <= period.end; it2++)
      {
        last = period.end > it2->end ? period.end : it2->end;
      }

      //fix end
      it->end = last;

      //remove periods in between
      v.erase(it + 1, it2);
    }
  }
}

//This function is used in tests. It builds
//the period given the string representation
//For instance " [ ] " the period is 1 - 3
//             " |   " the period is 1 - 1
static void BuildVectorForTest(vector<Period>& v, const char* s)
{
  DateTime begin = 0;
  int i = 0;
  while (s[i] != 0)
  {
    if (s[i] == '[')
    {
      //start
      begin = i;
    }
    else if (s[i] == ']')
    {
      //end
      AddPeriod(v, { begin, (DateTime)i });
    }
    else if (s[i] == '|')
    {
      //start==end
      AddPeriod(v, { (DateTime)i, (DateTime)i });
    }
    i++;
  }
}

void Test1()
{
  //[ ]
  vector<Period> v;
  AddPeriod(v, { 1, 3 });
  AddPeriod(v, { 2, 6 });
  AddPeriod(v, { 7, 10 });
  AddPeriod(v, { 8, 16 });
  vector<Period> v2 = { { 1, 6 },{ 7, 16 } };
  assert(equal(v.begin(), v.end(), v2.begin()));
}


void Test(const char* periods[], int size)
{
  vector<Period> v;
  for (int i = 0; i < size - 1; i++)
  {
    vector<Period> v2;
    BuildVectorForTest(v2, periods[i]);
    AddPeriod(v, v2.back());
  }

  vector<Period> v2;
  BuildVectorForTest(v2, periods[size - 1]);
  if (v2.size() == v.size())
  {
    assert(equal(v.begin(), v.end(), v2.begin()));
  }
  else
  {
    assert(false);
  }
}


void Test2()
{
  const char* periods[] =
  {
    " | ",
    "   | ",
    " | | " //aswer
  };
  Test(periods, sizeof(periods) / sizeof(periods[0]));
}


void Test3()
{
  const char* periods[] =
  {
    " [ ]",
    "     [ ]",
    " [       ]",
    " [       ]" //aswer
  };
  Test(periods, sizeof(periods) / sizeof(periods[0]));
}

void Test4()
{
  const char* periods[] =
  {
    " [ ]",
    "      |",
    " [ ]  |" //aswer
  };
  Test(periods, sizeof(periods) / sizeof(periods[0]));
}

void Test5()
{
  const char* periods[] =
  {
    " |    ",
    "     |",
    " [   ] ",
    " [   ] " //asnwer
  };
  Test(periods, sizeof(periods) / sizeof(periods[0]));
}
void Test6()
{
  const char* periods[] =
  {
    " [   ]             ",
    "       [   ]       ",
    "             [   ] ",
    "   [           ]   ",
    " [               ]   " //asnwer
  };
  Test(periods, sizeof(periods) / sizeof(periods[0]));
}


int main()
{
  Test6();
  Test5();
  Test4();

  Test1();
  Test2();
  Test3();

  return 0;
}