具体描述
代码
#include <bits/stdc++.h>
using namespace std;
string solution(int num, string data) {
vector<int> positions;
int sum = 0;
vector<int> index;
for (int i = 0; i < num; i++) {
if (data[i] != '.') index.push_back(i);
}
if (index.empty()) {
ostringstream ans;
ans << num << ":";
for (int i = 1; i <= num; i++) {
if (i > 1) ans << ",";
ans << i;
}
return ans.str();
}
for (int i = 1; i < index.size(); i++) {
int left = index[i - 1], right = index[i];
if (data[left] == 'L' && data[right] == 'R') { // LR case: All between are balanced.
for (int j = left + 1; j < right; j++) positions.push_back(j + 1);
sum += right - left - 1;
} else if (data[left] == 'R' && data[right] == 'L' && (left + right) % 2 == 0) { // RL case: Only one balanced.
positions.push_back((left + right) / 2 + 1);
sum++;
}
}
if (data[index[0]] == 'R') {
for (int i = 0; i < index[0]; i++) positions.push_back(i + 1);
sum += index[0];
}
if (data[index.back()] == 'L') {
for (int i = index.back() + 1; i < num; i++) positions.push_back(i + 1);
sum += num - 1 - index.back();
}
sort(positions.begin(), positions.end());
ostringstream ans;
ans << sum;
if (!positions.empty()) {
ans << ":";
for (size_t i = 0; i < positions.size(); i++) {
if (i > 0) ans << ",";
ans << positions[i];
}
}
return ans.str();
}
int main() {
cout << (solution(14, ".L.R...LR..L..") == "4:3,6,13,14") << endl;
cout << (solution(5, "R....") == "0") << endl;
cout << (solution(1, ".") == "1:1") << endl;
return 0;
}
思路
这个问题的核心是模拟多米诺骨牌的倒下过程,并确定哪些骨牌最终保持竖立。初始状态包括三种情况:
- "L" :表示该位置的骨牌将向左倒。
- "R" :表示该位置的骨牌将向右倒。
- "." :表示该位置的骨牌初始时保持竖立。
我们需要分析骨牌倒下的相互作用,尤其是如何处理骨牌之间的力平衡。目标是确定最终状态中保持竖立的骨牌位置,并输出它们的总数和具体位置。
问题分解
-
找到所有的 "L" 和 "R" 的位置:
这些位置是力的来源。我们遍历整个字符串,记录所有非 "." 的位置索引。 -
处理特殊情况:
- 如果整个字符串都是 "."(没有 "L" 或 "R"),则所有骨牌都保持竖立。
- 如果字符串中没有任何力作用的骨牌,输出所有位置并终止。
-
分析相邻的 "L" 和 "R" 的关系:
-
LR:
如果相邻的两个骨牌分别是 "L" 和 "R",它们之间的骨牌将全部保持竖立。 -
RL:
如果相邻的两个骨牌是 "R" 和 "L",需要判断它们之间的骨牌数是否为偶数:- 偶数:中间的一个骨牌会保持竖立,因为两边的力平衡。
- 奇数:所有骨牌都会倒下,没有骨牌竖立。
-
RR / LL:
同向的骨牌之间不会有保持竖立的骨牌。
-
-
处理头部和尾部:
- 头部:"R" 出现在最左边时,它左边的所有骨牌都会被推倒。
- 尾部:"L" 出现在最右边时,它右边的所有骨牌都会被推倒。
-
输出处理:
将所有保持竖立的骨牌位置排序,并构建输出字符串。
解法分析
主要步骤:
-
遍历字符串,记录所有非 "." 的索引。
-
根据索引之间的关系处理骨牌竖立情况:
- 处理 LR 和 RL 情况。
- 处理头部和尾部的特殊情况。
-
构建最终输出字符串。
分步解析
vector<int> index;
for (int i = 0; i < num; i++) {
if (data[i] != '.') index.push_back(i);
}
- 作用:记录所有非 "." 的索引。
if (index.empty()) {
ostringstream ans;
ans << num << ":";
for (int i = 1; i <= num; i++) {
if (i > 1) ans << ",";
ans << i;
}
return ans.str();
}
- 作用:处理全是 "." 的情况。所有骨牌都保持竖立。
for (int i = 1; i < index.size(); i++) {
int left = index[i - 1], right = index[i];
if (data[left] == 'L' && data[right] == 'R') {
// LR: All between remain upright
} else if (data[left] == 'R' && data[right] == 'L' && (left + right) % 2 == 0) {
// RL: Only one in the middle remains upright (if distance is even)
}
}
- 作用:核心逻辑,分析不同的骨牌对之间的力作用。
ostringstream ans;
ans << sum;
if (!positions.empty()) {
ans << ":";
for (size_t i = 0; i < positions.size(); i++) {
if (i > 0) ans << ",";
ans << positions[i];
}
}
- 作用:将结果拼接成所需格式。
时空复杂度
-
时间复杂度:
- 遍历字符串一次:O(n)O(n)O(n)。
- 对索引向量进行排序:O(klogk)O(k \log k)O(klogk),其中 kkk 是竖立骨牌的数量。
- 总时间复杂度:O(n+klogk)O(n + k \log k)O(n+klogk)。由于 k≤nk \leq nk≤n,整体复杂度为 O(nlogn)O(n \log n)O(nlogn)。
-
空间复杂度:
- 额外使用了两个向量来存储索引和保持竖立的骨牌位置:O(n)O(n)O(n)。
总结
这道题本质上是一个模拟过程,要求我们根据骨牌的倒下状态("L" 和 "R")来推导出最终的竖立状态。在模拟过程中,关键在于如何处理力的传播以及力的平衡。
处理问题时,边界条件的正确性往往是影响结果的关键:
- 字符串全是 "." 时,所有骨牌都应保持竖立。
- 头部 和 尾部 的特殊情况:若头部有 "R",则左边所有骨牌都会被推倒;若尾部有 "L",则右边所有骨牌都会被推倒。
这些边界条件一旦忽略,就会导致最终结果错误。通过细致的处理这些边界条件,我深刻体会到在解决实际问题时,如何在细节上做到严谨。