一、双指针算法
发现受力总可以分为一个一个的区间,这个区间的中间是不受力的'.'
,两边是力。
若区间形如[L....L][L....L][L....L],则会把区间中的所有'.'
改为'L'
;
若区间形如[R....L][R....L][R....L],则会把区间中的左一半元素改为'L'
,右一半元素改为'R'
;
若区间形如[R....R][R....R][R....R],则会把区间中的全部.
改为'R'
;
若区间形如[L....R][L....R][L....R],则区间中的牌子不会受力,无需更改。
class Solution {
public:
// 把[a, b]全部都染色成ch
void trans(string& s, int a, int b, char ch)
{
if (a <= b)
{
for (int i = a; i <= b; ++i)
{
s[i] = ch;
}
}
}
string pushDominoes(string dominoes)
{
int n = dominoes.size();
string ret = dominoes;
int previdx = -1;
char prevch = 'N';
// 一个区间形如[R....L]或[L.....L]或[R.....R]或[L....R]
for (int i = 0; i < n; ++i)
{
// 如果当前区间右边的力是'L'
if (dominoes[i] == 'L')
{
// 如果区间左端点是'N' 前继还没进串 那么就把区间全部染色成'L'
// 表示向左推
if (prevch == 'N')
{
trans(ret, previdx + 1, i - 1, 'L');
}
// [L...L] 全部向左推
else if (prevch == 'L')
{
trans(ret, previdx + 1, i - 1, 'L');
}
// [R....L] 左边一半向右 右边一半向左
else
{
int cnt = (i - previdx - 1) / 2;// 左右应当染色的个数
trans(ret, previdx + 1, previdx + cnt, 'R');
trans(ret, i - cnt, i - 1, 'L');
}
previdx = i;
prevch = 'L';
}
else if (dominoes[i] == 'R')
{
// [R......R] 全部染色成向右
if (prevch == 'R')
{
trans(ret, previdx + 1, i - 1, 'R');
}
// 其他情况不用做处理
previdx = i;
prevch = 'R';
}
}
if (prevch == 'R')
{
// 如果前一个是'R' 走到结尾这个'R'还没处理 单独处理一下
trans(ret, previdx + 1, n - 1, 'R');
}
return ret;
}
};
二、宽度优先搜索
本题类似一个扩散过程,注意到如果一个牌子同一时刻只受一个力的影响,则会倾倒;
所以可以维护timetimetime数组表示牌子确定受力的时间,一个队列表示已经受力了正在等待处理的牌子的下标,forceforceforce数组用来存储每个牌子的受力情况。
每次处理队头元素时,若其受力数仅为1,则把它倾倒,即把ret[q.front()]ret[q.front()]ret[q.front()]改为对应方向,然后看看它会往哪边倾倒,然后考虑它倾倒方向的牌子neidxneidxneidx;
若neidxneidxneidx还未被受力影响过,那么把它的受力时间更新为time[q.front()] + 1
,并且把它的受力情况更新到force[neidx]
中,然后把它入队列待处理,
若neidxneidxneidx的上次受力时间就等于time[q.front()] + 1
,也就是说它同一时刻又受到了一个相反方向的力,它已经在队列里了,把它的受力情况更新一下;
若不是同一时间受的力,根据队列的性质一定是在当前时间之后受的力,根据多米诺骨牌的性质,第一次在同一时间确定受力后它就会倒了,再后面受的力不能再影响倒了的多米诺骨牌,不处理即可。
class Solution {
public:
string pushDominoes(string dominoes)
{
int n = dominoes.size();
queue<int> q;
vector<int> time(n, -1);// 表示牌子确定翻到或确定不翻倒的时间
vector<string> force(n);// 表示当前板子受的力 当仅受单侧力的时候会翻转
string ret(n, '.');
for (int i = 0; i < n; ++i)
{
if (dominoes[i] != '.')
{
q.push(i);// 受力倒了并且下一秒会影响临近的牌的下标入队列
time[i] = 0;// 标记它们是0时刻受力的
force[i].push_back(dominoes[i]);
}
}
while (!q.empty())
{
int idx = q.front();
q.pop();
// 如果受力个数为单数 则会倒
if (force[idx].size() == 1)
{
// 先处理当前时刻的受力
char f = force[idx][0];
ret[idx] = f;
// 找到它会临近影响的牌
int neidx = (f == 'L' ? idx - 1 : idx + 1);
if (neidx >= 0 && neidx < n)
{
// 说明这个牌子还没被受力影响过 还没入过队列
if (time[neidx] == -1)
{
// 更新它的受力时间为当前受力时间加1
time[neidx] = time[idx] + 1;
// 入队 受力加上去
q.push(neidx);
force[neidx].push_back(f);
}
// 否则若在同一时刻受到了相邻的别的方向(只能是右方向)
else if (time[neidx] == time[idx] + 1)
{
force[neidx].push_back(f);
}
}
}
}
return ret;
}
};