问题描述
小S玩起了多米诺骨牌,他排列了一行骨牌,并可能将某些骨牌向左或向右推倒。随着骨牌连锁反应的进行,一些骨牌可能因为左右两侧受力平衡而保持竖立。现在小S想要知道在所有动作完成后,哪些骨牌保持竖立。
给定一个表示骨牌初始状态的字符串,其中:
- "L" 表示该位置的骨牌将向左倒。
- "R" 表示该位置的骨牌将向右倒。
- "." 表示该位置的骨牌初始时保持竖立。
模拟整个骨牌倒下的过程,求出最终仍然保持竖立的骨牌的数目和位置。
测试样例
样例1:
输入:
num = 14,data = ".L.R...LR..L.."输出:'4:3,6,13,14'
样例2:
输入:
num = 5,data = "R...."输出:'0'
样例3:
输入:
num = 1,data = "."输出:'1:1'
方法一:广度优先搜索
思路
当时间为 0 时,部分骨牌会受到一个初始的向左或向右的力而翻倒。过了 1 秒后,这些翻倒的骨牌会对其周围的骨牌施加一个力。具体表现为:
- 向左翻倒的骨牌,如果它有直立的左边紧邻的骨牌,则会对该直立的骨牌施加一个向左的力。
- 向右翻倒的骨牌,如果它有直立的右边紧邻的骨牌,则会对该直立的骨牌施加一个向右的力。
接下去需要分析这些 1 秒时受力的骨牌的状态。如果仅受到单侧的力,它们会倒向单侧;如果受到两个力,则会保持平衡。再过 1秒后,这些新翻倒的骨牌又会对其他直立的骨牌施加力,而不会对正在翻倒或已经翻倒的骨牌施加力。
这样的思路类似于广度优先搜索。我们用一个队列 q 模拟搜索的顺序;数组 time 记录骨牌翻倒或者确定不翻倒的时间,翻倒的骨牌不会对正在翻倒或者已经翻倒的骨牌施加力;数组 force 记录骨牌受到的力,骨牌仅在受到单侧的力时会翻倒。
def solution(num, data):
n = num
q = deque()
time = [-1] * n
force = [[] for _ in range(n)]
# 初始化队列和时间数组
for i, f in enumerate(data):
if f != '.':
q.append(i)
time[i] = 0
force[i].append(f)
res = ['.'] * n
while q:
i = q.popleft()
if len(force[i]) == 1:
res[i] = f = force[i][0]
ni = i - 1 if f == 'L' else i + 1
if 0 <= ni < n:
t = time[i]
if time[ni] == -1:
q.append(ni)
time[ni] = t + 1
force[ni].append(f)
elif time[ni] == t + 1:
force[ni].append(f)
# 记录竖立的骨牌
standing_indices = []
for i in range(n):
if res[i] == '.':
standing_indices.append(i + 1) # 位置从1开始计数
# 生成输出字符串
if not standing_indices:
return '0'
else:
return f"{len(standing_indices)}:" + ",".join(map(str, standing_indices))
方法二:模拟
思路
我们可以枚举所有连续的没有被推动的骨牌,根据这段骨牌的两边骨牌(如果有的话)的推倒方向决定这段骨牌的最终状态:
- 如果两边的骨牌同向,那么这段连续的竖立骨牌会倒向同一方向。
- 如果两边的骨牌相对,那么这段骨牌会向中间倒。
- 如果两边的骨牌相反,那么这段骨牌会保持竖立。
特别地,如果左侧没有被推倒的骨牌,则假设存在一块向左倒的骨牌;如果右侧没有被推倒的骨牌,则假设存在一块向右倒的骨牌。这样的假设不会破坏骨牌的最终状态,并且边界情况也可以落入到上述三种情况中。
我们可以使用两个指针 i 和 j 来枚举所有连续的没有被推动的骨牌,left 和 right 表示两边骨牌的推倒方向。根据上述三种情况来计算骨牌的最终状态。
def solution(num, data):
s = list(data)
n, i, left = len(s), 0, 'L'
while i < n:
j = i
while j < n and s[j] == '.': # 找到一段连续的没有被推动的骨牌
j += 1
right = s[j] if j < n else 'R'
if left == right: # 方向相同,那么这些竖立骨牌也会倒向同一方向
while i < j:
s[i] = right
i += 1
elif left == 'R' and right == 'L': # 方向相对,那么就从两侧向中间倒
k = j - 1
while i < k:
s[i] = 'R'
s[k] = 'L'
i += 1
k -= 1
left = right
i = j + 1
# 记录竖立的骨牌
standing_indices = []
for i in range(n):
if s[i] == '.':
standing_indices.append(i + 1) # 位置从1开始计数
# 生成输出字符串
if not standing_indices:
return '0'
else:
return f"{len(standing_indices)}:" + ",".join(map(str, standing_indices))