BFS
逻辑:元素倒下后,状态不再改变。每个元素对应唯一的倒下时间。
//官方题解
class Solution {
public String pushDominoes(String dominoes) {
int n = dominoes.length();
Deque<Integer> queue = new ArrayDeque<Integer>();
int[] time = new int[n];
Arrays.fill(time, -1);
List<Character>[] force = new List[n];
for (int i = 0; i < n; i++) {
force[i] = new ArrayList<Character>();
}
//初始入队:正在倒下的元素
for (int i = 0; i < n; i++) {
char f = dominoes.charAt(i);
if (f != '.') {
queue.offer(i);
time[i] = 0;
force[i].add(f);
}
}
//char数组便于字符串替换
char[] res = new char[n];
Arrays.fill(res, '.');
while (!queue.isEmpty()) {
int i = queue.poll();
//【巧妙】只受到一个力才会倒
//受力有且仅有两种,时间只会先后或同时
//先后:后作用力不会施加于已有作用力的元素
//同时:元素出队时,已到下一时间,力皆作用完毕
if (force[i].size() == 1) {
char f = force[i].get(0);
res[i] = f;
//根据力的方向确定下一个倒下的元素ni
int ni = f == 'L' ? i - 1 : i + 1;
//元素在数组范围内
if (ni >= 0 && ni < n) {
int t = time[i];
if (time[ni] == -1) {
//根据倒下时间筛选,未确定时间的入队
queue.offer(ni);
//入队后再确定时间
time[ni] = t + 1;
force[ni].add(f);
} else if (time[ni] == t + 1) { //同时
force[ni].add(f);
}
}
}
}
return new String(res);
}
}
双指针
转化:【区间问题】
区间:两端both为正在倒下的骨牌,它们包裹着若干个竖立的骨牌
状态:
- 如果两边的骨牌同向,那么这段连续的竖立骨牌会倒向同一方向。
- 如果两边的骨牌相对,那么这段骨牌会向中间倒。
- 如果两边的骨牌相反,那么这段骨牌会保持竖立。
编程技巧:给dominoes
最左边添加一个“L”,最右边添加一个“R”
//官方题解
class Solution {
public String pushDominoes(String dominoes) {
char[] s = dominoes.toCharArray();
int n = s.length, i = 0;
//【巧妙】不直接扩大dominoes,而是单独设区间端点left='L'
char left = 'L';
while (i < n) {
int j = i;
//找右端点:滤去中间竖立骨牌
while (j < n && s[j] == '.') { // 找到一段连续的没有被推动的骨牌
j++;
}
//【巧妙】不直接扩大dominoes,而是单独设区间端点right='R'
char right = j < n ? s[j] : 'R';
//判断区间两端方向
if (left == right) { // 方向相同,那么这些竖立骨牌也会倒向同一方向
while (i < j) {
s[i++] = right;
}
} else if (left == 'R' && right == 'L') { // 方向相对,那么就从两侧向中间倒
int k = j - 1;
while (i < k) {
s[i++] = 'R';
s[k--] = 'L';
}
}
//迭代下个区间
left = right;
i = j + 1;
}
return new String(s);
}
}