Skip to content

Instantly share code, notes, and snippets.

@xphoniex
Created January 6, 2019 19:48
Show Gist options
  • Save xphoniex/ec81075f42367c98e97e74726191fd8c to your computer and use it in GitHub Desktop.
Save xphoniex/ec81075f42367c98e97e74726191fd8c to your computer and use it in GitHub Desktop.
think-cell
/*
Task Description
interval_map<K,V> is a data structure that efficiently associates intervals of keys of type K with values of type V. Your task is to implement the assign member function of this data structure, which is outlined below.
interval_map<K, V> is implemented on top of std::map. In case you are not entirely sure which functions std::map provides, what they do and which guarantees they provide, we provide an excerpt of the C++ standard here:
Each key-value-pair (k,v) in the std::map means that the value v is associated with the interval from k (including) to the next key (excluding) in the std::map.
Example: the std::map (0,'A'), (3,'B'), (5,'A') represents the mapping
0 -> 'A'
1 -> 'A'
2 -> 'A'
3 -> 'B'
4 -> 'B'
5 -> 'A'
6 -> 'A'
7 -> 'A'
... all the way to numeric_limits<int>::max()
The representation in the std::map must be canonical, that is, consecutive map entries must not have the same value: ..., (0,'A'), (3,'A'), ... is not allowed. Initially, the whole range of K is associated with a given initial value, passed to the constructor of the interval_map<K,V> data structure.
Key type K
besides being copyable and assignable, is less-than comparable via operator<
is bounded below, with the lowest value being std::numeric_limits<K>::lowest()
does not implement any other operations, in particular no equality comparison or arithmetic operators
Value type V
besides being copyable and assignable, is equality-comparable via operator==
does not implement any other operations
*/
#include <map>
#include <limits>
#include <ctime>
template<typename K, typename V>
class interval_map {
std::map<K,V> m_map;
public:
// constructor associates whole range of K with val by inserting (K_min, val)
// into the map
interval_map( V const& val) {
m_map.insert(m_map.end(),std::make_pair(std::numeric_limits<K>::lowest(),val));
}
// Assign value val to interval [keyBegin, keyEnd).
// Overwrite previous values in this interval.
// Conforming to the C++ Standard Library conventions, the interval
// includes keyBegin, but excludes keyEnd.
// If !( keyBegin < keyEnd ), this designates an empty interval,
// and assign must do nothing.
void assign( K const& keyBegin, K const& keyEnd, V const& val ) {
if (!(keyBegin < keyEnd)) return;
std::pair<K,V> beginExtra;
std::pair<K,V> endExtra;
bool beginHasExtra = false;
bool endHasExtra = false;
typename std::map<K,V>::const_iterator itBegin;
itBegin = m_map.lower_bound(keyBegin);
if ( itBegin!=m_map.end() && keyBegin < itBegin->first ) {
if (itBegin != m_map.begin()) {
beginHasExtra = true;
--itBegin;
beginExtra = std::make_pair(itBegin->first, itBegin->second);
}
// openRange for erase is prevIterator
// insert (prevIterator->first, prevIterator->second) as well!
}
typename std::map<K,V>::const_iterator itEnd;
itEnd = m_map.lower_bound(keyEnd);
if ( itEnd!=m_map.end() && keyEnd < itEnd->first ) {
endHasExtra = true;
typename std::map<K,V>::const_iterator extraIt = itEnd;
--extraIt;
endExtra = std::make_pair(keyEnd, extraIt->second);
// closeRange for erase is this iterator
// insert (keyEnd, prevIterator->second) as well!
}
// 4 canonical conflicts:
// beginExtra w/ mid
// before-mid w/ mid (beginHasExtra==false)
// mid w/ endExtra
// mid w/ after-mid (endHasExtra==false)
bool insertMid = true;
if (beginHasExtra) {
if (beginExtra.second == val)
insertMid = false;
} else {
if (itBegin != m_map.begin()) {
typename std::map<K,V>::const_iterator beforeMid = itBegin;
--beforeMid;
if (beforeMid->second == val)
insertMid = false;
}
}
if (endHasExtra) {
if ( (insertMid && endExtra.second == val) || (!insertMid && endExtra.second == beginExtra.second) )
endHasExtra = false;
} else {
if ( (insertMid && itEnd!=m_map.end() && itEnd->second == val) || (!insertMid && itEnd!=m_map.end() && itEnd->second == beginExtra.second) )
itEnd = m_map.erase(itEnd);
}
itBegin = m_map.erase(itBegin, itEnd);
if (beginHasExtra)
itBegin = m_map.insert(itBegin, beginExtra);
if (insertMid)
itBegin = m_map.insert(itBegin, std::make_pair(keyBegin, val));
if (endHasExtra)
m_map.insert(itBegin, endExtra);
// INSERT YOUR SOLUTION HERE
}
// look-up of the value associated with key
V const& operator[]( K const& key ) const {
return ( --m_map.upper_bound(key) )->second;
}
};
@rllbe
Copy link

rllbe commented Oct 29, 2024

@rllbe There are perverts who like to look for their own kind and mock those who do not suit them. They get pleasure from it. Such people use any excuse to create a stratification of society on some grounds, this is what Hitler did with his prejudices, this is how castes exist in India, this is how in the USA in the past there were prejudices based on skin color, and in Rus' there was serfdom, this is how people occupying certain government positions abuse their position while ordinary people barely cope in order to feed themselves and pay all their housing bills. If you want to participate in a casino where it is known in advance that the probability of each player losing is high and the casino itself never loses anything, then this is your choice. Personally, I do not enjoy participating in such "role-playing games"!

Not sure who you're talking about. If it's me then I'd like to say I'm absolutely not like that. I was just an average person knowing nothing and only want to go through a general recruitment process. Finding solutions on the web after failing a screening is a thing everybody would do, be it to improve oneself or just feed one's curiosity.
But if it's the company then I would like to say it's way worse than what you've described. They probably even mark a correct answer as "correctness problem". You lose to the casino even by winning the game. They made prejudices based on nothing instead of anything like skin color. The closest event I could imagine is so called "Fengqiao Experience" in China. I mean people who want to take the test should Google it first and read something like this gist to be encouraged to deny the company as early as possible to prevent unworthy loss of time and privacy.

@alexismailov2
Copy link

@rllbe I am about that company! If you will try to find information about their interval map you will find rejections from iso for adding interval map to standard. And I think a few people from that company got depressed, which is why this whole story happened. If you look closely at the company's growth, you'll see that it's very slow, if you look at the company's products, you'll see that most of them are not necessary for the Microsoft Office product. All of these are puzzles of one big picture of the mental state of all the organizers of this recruitment process... But such companies a lot in the world all people should just:

  • check glassdors review first
  • check any info about that test in the internet
  • ask yourself Is it reasonable to do this test since on leetcode there is ~1500 more useful and more interesting problems(If answer YES(130000€) be thruth with yourself it is not equal to interest of solving c++ problems because there is a lot of professional platforms to do that)
  • salary rates depends mostly on taxes which in eu ~47% may be it is not a good idea to get 130000€ for paying half taxes since half of that amount equals to buy a small apartment instead of paying loans 20-30 years… There is UAE where salaries ~10k$ without taxes…
  • Think by future opportunities not by kidding with your life and that will given you a correct answer!

@lvdpower
Copy link

lvdpower commented Nov 8, 2024

If the new range intersects with the old one, should it overwrite the old range within the new one or should it ignore it?

for example first assign is -300:B 300:A (so all keys between -300 and 299 are B.
second assign fo example -500:F 500:S - what it this?
variant 1 - it just owerwrite previous -300:300 range with new bigger range - so in finish only one range will be in map
or
variant 2- it write ranges only -500 till -300 and 300 to 500 with new data (but keep old range -300:B 300:A untouched) - so in finish 3 ranges will be in map

Please answer on this!

@PavanPongle
Copy link

Today, I attempted the test and unfortunately could not clear it.
I failed at check 1 "Type requirements: You must adhere to the specification of the key and value type given above."
I follow the given constraint in the problem that only move and copy operation/constructor allowed and only < or == . No other operation I used.
Also, I used custom type to explicitly delete other operations/constructors. In addition to this, I checked against the static assert on "std::is_default_constructible::value" and both key and value types passed.

I would like to know from my implementation what was required apart from what I have written.
Could you please share some insights on it? Or point me to a resource where I can understand this in more depth about the concept?
I am attaching my solution Please review it once.
I am looking forward to hearing from you.

`
template
void assign(K const& keyBegin, K const& keyEnd, V_forward&& val)
requires (std::is_same<std::remove_cvref_t<V_forward>, V>::value)
{
// invalid case
if (!(keyBegin < keyEnd))
{
return;
}

	//limit is whole map
	const auto& k_low = std::numeric_limits<K>::lowest();
	const auto& k_max = std::numeric_limits<K>::max();
	if (!(k_low < keyBegin) && !(keyBegin < k_low) &&
		!(k_max < keyEnd) && !(keyEnd < k_max))
	{
		m_map.clear();
		m_valBegin = std::forward<V_forward>(val);

		return;
	}

	//first assignment
	if (m_map.empty())
	{
		if (val == m_valBegin)
			return;

		(void) m_map.insert_or_assign(K(keyBegin), std::forward<V_forward>(val));
		(void) m_map.insert_or_assign(K(keyEnd), std::forward<V_forward>(m_valBegin));

		return;
	}

	using itr_t = std::map<K, V>::iterator;
	const auto& [key_begin_bounds_l, key_begin_bounds_u]  = m_map.equal_range(keyBegin);
	const auto& [key_end_bounds_l, key_end_bounds_u] = m_map.equal_range(keyEnd);

	bool is_key_begin_presend = key_begin_bounds_l != key_begin_bounds_u;
	bool is_key_end_presend = key_end_bounds_l != key_end_bounds_u;

	
	itr_t new_key_begin = key_begin_bounds_l;
	itr_t new_key_end = key_end_bounds_l;

	//If key is present then lower bound is where it found


	//Check if previous node value is same as current val
	bool is_pre_key_begin_val_same = false;
	if (key_begin_bounds_l != m_map.begin())
	{
		// if previous node's value same as current range value then to avoid duplication of val, skip addition of keyBegin
		auto it = key_begin_bounds_l;
		if ((--it)->second == val)
		{
			is_pre_key_begin_val_same = true;
		}
	}

	if (key_end_bounds_u != m_map.end())
	{
		// if next value is end of some range then to avoid duplication of val, skip addition of it
		if (key_end_bounds_u->second == m_valBegin)
		{
			if (is_pre_key_begin_val_same)
				// if it is in same key's range then e
				is_key_end_presend = true;
			else if (is_key_end_presend && key_end_bounds_l->second == val) //means key is being place in existing range
			{
				new_key_end = key_end_bounds_u;
			}
		}
	}

	if (!is_key_begin_presend && key_begin_bounds_l != m_map.begin())
	{
		// if previous node's value same as current range value then to avoid duplication of val, skip addition of keyBegin
		if (is_key_end_presend && is_pre_key_begin_val_same)
			return;
	}

	if (!is_key_end_presend)
	{
		V& last_range_val = m_valBegin;

		auto it = key_end_bounds_u;
		if (it != m_map.begin()) {
			last_range_val = (--it)->second;
		}

		if (!(last_range_val == val))
			// endKey not present in map, insert at end
			new_key_end = m_map.insert_or_assign(key_end_bounds_l, keyEnd, last_range_val);
	}

	if (!is_pre_key_begin_val_same)
	{
		if (!is_key_begin_presend)
		{
			new_key_begin = m_map.insert_or_assign(key_begin_bounds_l, keyBegin, std::forward<V_forward>(val));
			is_key_begin_presend = true;
		}
		else if ((key_begin_bounds_u == m_map.end() && key_begin_bounds_l != m_map.begin()) ||
			!(key_begin_bounds_l->second == val))
		{
			//its last position modify it here
			new (&key_begin_bounds_l->second) V(std::forward<V_forward>(val));
		}
	}
	else
	{
		// if last key present and incase KeyBeing match with existing keyEnd
		is_key_begin_presend = false;
	}

	if (is_key_begin_presend)
		std::advance(new_key_begin, 1);

	m_map.erase(new_key_begin, new_key_end);
	
}

`

@NAmanbek
Copy link

NAmanbek commented Jan 5, 2025

I have two solutions for this task. First one passed successfully, due to that i had no chance to check if second solution is also correct. If somebody have a link to online test and wants to check my second solution write me a pm.

i have test link

Send me your direct contacts

Hi, could you check please my code: if (!(keyBegin < keyEnd)) return;

    // Find existing value at keyEnd
    auto itEnd = m_map.upper_bound(keyEnd);
    V valueAfter;
    if (itEnd == m_map.begin()) {
        valueAfter = m_valBegin;
    } else {
        valueAfter = std::prev(itEnd)->second;
    }

    // Find existing value at keyBegin
    auto itBegin = m_map.upper_bound(keyBegin);
    V valueBefore;
    if (itBegin == m_map.begin()) {
        valueBefore = m_valBegin;
    } else {
        valueBefore = std::prev(itBegin)->second;
    }

    // Remove all points in [keyBegin, keyEnd)
    itBegin = m_map.lower_bound(keyBegin);
    itEnd = m_map.lower_bound(keyEnd);
    m_map.erase(itBegin, itEnd);

    // If new value differs from value before keyBegin, add entry
    if (val != valueBefore) {
        m_map.insert_or_assign(keyBegin, std::forward<V_forward>(val));
    }

    // If value after keyEnd differs from new value, add entry
    if (valueAfter != val) {
        m_map[keyEnd] = valueAfter;
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment