split-stay
Split Stay — Two-Listing Date Coverage¶
一亩三分地
Given a set of Airbnb listings, each with a list of available days, and a requested date range, return every pair of two distinct listings whose combined availability covers the range with no overlap (the first listing covers a contiguous prefix, the second covers the remainder).
Example
A - [1,2,3,6,7,10,11]
B - [3,4,5,6,8,9,10,13]
C - [7,8,9,10,11]
range = [3, 11]
expected = [(B, C)] # B covers 3-6, C covers 7-11
暴力解法 brute force¶
直接解法:遍历每一对组合,然后判断是否可以覆盖[start, end]区间,判断方法也比较简单,A先和B先各试一次就行
时间复杂度 N^2 \times M
bitmask¶
用bitmask表示每个民宿的覆盖时间,然后同样遍历组合,用位操作帮助加速判断
有一个技巧是统计每一个民宿prefix和suffix的连续长度,这样两个民宿的prefix+suffix(或相反)相加和end-start+1比较即可
prefix length
- 根据bitmask获取~missing,注意这里判断下如果missing为0,说明是全部覆盖的
- 对missing找到最低位的1,说明更低的部分是连续的0,也就是民宿连续覆盖的天数
- 最低位的1 bit位置-1即可
获取最低位的1方法如下,这里有个基础知识,-x=~x+1,即对x取反然后+1,所以两个相与,得到就是最低位的1
x & -x
suffix length
- 获取missing,同样进行判0
- 获取最高位的1,说明更高位都是连续0
- length - pos即可
最高位的1 Python用 bit_length()即可
def find_split_stays(
availability: Mapping[str, List[int]],
start_day: int,
end_day: int
) -> List[Tuple[str, ...]]:
# convert day to bitmask
prefix = {}
suffix = {}
length = end_day - start_day + 1
days_mask = (1 << length) - 1
for key, days in availability.items():
bitmask = 0
for day in days:
if day >= start_day and day <= end_day:
delta = day - start_day
bitmask |= (1 << delta)
"""
prefix_len continuous 1 bit from low position
1. missing = mask ^ all days
2. find missing lowest 1 pos
3. pos - 1
"""
missing = days_mask ^ bitmask
if missing == 0:
prefix_len = length
else:
lowest_bit = missing & -missing
prefix_len = lowest_bit.bit_length() - 1
"""
suffix_len continuosu 1 bits from high position
1. get missing
2. get missing highest 1 bit pos
3. length - pos
"""
if missing == 0:
suffix_len = length
else:
suffix_len = length - missing.bit_length()
prefix[key] = prefix_len
suffix[key] = suffix_len
# print(start_day, end_day, key, days, prefix_len, suffix_len)
# traverse and check each pair
result = []
length = end_day - start_day + 1
for house, pre in prefix.items():
if pre == length:
result.append(tuple([house]))
for a, b in combinations(availability.keys(), 2):
if prefix[a] == length:
continue
if prefix[b] == length:
continue
# check overlap
valid = False
if prefix[a] > 0 and suffix[b] > 0 and prefix[a] + suffix[b] >= length:
valid = True
if suffix[a] > 0 and prefix[b] > 0 and suffix[a] + prefix[b] >= length:
valid = True
if valid:
result.append(tuple([a, b]))
return result