text: // https://www.coursera.org/learn/c-plus-plus-white/programming/4FQn4/imiena-i-familii-1
  
  #include <algorithm>
  #include <map>
  #include <vector>
  #include <string>
  #include <iostream>
  
  using namespace std;
  
  vector<string> FindNamesHistory(const map<int, string> &names_by_year,
                                  int year)
  {
      vector<string> names;
      // перебираем всю историю в хронологическом порядке
      for (const auto &item : names_by_year)
      {
          // если очередное имя не относится к будущему и отличается от предыдущего,
          if (item.first <= year && (names.empty() || names.back() != item.second))
          {
              // добавляем его в историю
              names.push_back(item.second);
          }
      }
      return names;
  }
  
  string BuildJoinedName(vector<string> names)
  {
      // нет истории — имя неизвестно
      if (names.empty())
      {
          return "";
      }
      // меняем прямой хронологический порядок на обратный
      reverse(begin(names), end(names));
  
      // начинаем строить полное имя с самого последнего
      string joined_name = names[0];
  
      // перебираем все последующие имена
      for (int i = 1; i < names.size(); ++i)
      {
          if (i == 1)
          {
              // если это первое «историческое» имя, отделяем его от последнего скобкой
              joined_name += " (";
          }
          else
          {
              // если это не первое имя, отделяем от предыдущего запятой
              joined_name += ", ";
          }
          // и добавляем очередное имя
          joined_name += names[i];
      }
  
      // если в истории было больше одного имени, мы открывали скобку — закроем её
      if (names.size() > 1)
      {
          joined_name += ")";
      }
      // имя со всей историей готово
      return joined_name;
  }
  
  // см. решение предыдущей задачи
  string BuildFullName(const string &first_name, const string &last_name)
  {
      if (first_name.empty() && last_name.empty())
      {
          return "Incognito";
      }
      else if (first_name.empty())
      {
          return last_name + " with unknown first name";
      }
      else if (last_name.empty())
      {
          return first_name + " with unknown last name";
      }
      else
      {
          return first_name + " " + last_name;
      }
  }
  
  class Person
  {
  public:
      Person(const string &first_name, const string &last_name, int birth)
      {
          ChangeFirstName(birth, first_name);
          ChangeLastName(birth, last_name);
          year_of_birth = birth;
      }
  
      void ChangeFirstName(int year, const string &first_name)
      {
          if (year >= year_of_birth)
          {
              first_names[year] = first_name;
          }
      }
  
      void ChangeLastName(int year, const string &last_name)
      {
          if (year >= year_of_birth)
          {
              last_names[year] = last_name;
          }
      }
  
      string GetFullName(int year) const
      {
          // найдём историю изменений имени и фамилии
          // в данном случае это излишне, так как нам достаточно узнать последние
          // имя и фамилию, но почему бы не использовать готовые функции?
          const vector<string> first_names_history = FindFirstNamesHistory(year);
          const vector<string> last_names_history = FindLastNamesHistory(year);
  
          // подготовим данные для функции BuildFullName: возьмём последние имя
          // и фамилию или оставим их пустыми, если они неизвестны
          string first_name;
          string last_name;
  
          if (year < year_of_birth)
          {
              return "No person";
          }
  
          if (!first_names_history.empty())
          {
              first_name = first_names_history.back();
          }
          if (!last_names_history.empty())
          {
              last_name = last_names_history.back();
          }
          return BuildFullName(first_name, last_name);
      }
  
      string GetFullNameWithHistory(int year) const
      {
          if (year < year_of_birth)
          {
              return "No person";
          }
  
          // получим полное имя со всей историей
          const string first_name = BuildJoinedName(FindFirstNamesHistory(year));
          // получим полную фамилию со всей историей
          const string last_name = BuildJoinedName(FindLastNamesHistory(year));
          // объединим их с помощью готовой функции
          return BuildFullName(first_name, last_name);
      }
  
  private:
      vector<string> FindFirstNamesHistory(int year) const
      {
          return FindNamesHistory(first_names, year);
      }
      vector<string> FindLastNamesHistory(int year) const
      {
          return FindNamesHistory(last_names, year);
      }
  
      map<int, string> first_names;
      map<int, string> last_names;
      int year_of_birth = 0;
  };
  
  int main()
  {
      Person person("Polina", "Sergeeva", 1960);
      for (int year : {1959, 1960})
      {
          cout << person.GetFullNameWithHistory(year) << endl;
      }
  
      person.ChangeFirstName(1965, "Appolinaria");
      person.ChangeLastName(1967, "Ivanova");
      for (int year : {1965, 1967})
      {
          cout << person.GetFullNameWithHistory(year) << endl;
      }
  
      return 0;
  }