题目解析:多米诺骨牌均衡状态 | 豆包MarsCode AI刷题

126 阅读7分钟

因为这道题想了很久才写出来,所以来写个题解,不过我的想法可能有点复杂,接下来进入正题。

问题描述

小S玩起了多米诺骨牌,他排列了一行骨牌,并可能将某些骨牌向左或向右推倒。随着骨牌连锁反应的进行,一些骨牌可能因为左右两侧受力平衡而保持竖立。现在小S想要知道在所有动作完成后,哪些骨牌保持竖立。

给定一个表示骨牌初始状态的字符串,其中:

  • "L" 表示该位置的骨牌将向左倒。
  • "R" 表示该位置的骨牌将向右倒。
  • "." 表示该位置的骨牌初始时保持竖立。

模拟整个骨牌倒下的过程,求出最终仍然保持竖立的骨牌的数目和位置。

解题思路

题目分析

根据题意可知,这是一道模拟题,我们需要模拟每一个骨牌的位置,对于若干连续的竖立骨牌(也可以是1个或者0个),它(们)是否倾倒取决于它(们)左右两边的骨牌状态,根据左右两边骨牌的不同,我们可以分为以下三种情况:

第一种:左边为L骨牌,右边为R骨牌

image.png

在该种情况下,中间的所有竖立骨牌均不会倾倒,此时得到竖立骨牌的数量为R的位置下标 - L的位置下标 - 1,即6 - 1 - 1 = 4

第二种:左边为R骨牌,右边为L骨牌

在该种情况下,会出现两种可能结果:

  • 第 1 种可能:存在一个竖立的骨牌 image.png 在以上这种情况下,中间的骨牌会因为左右两边的倾倒骨牌而平衡,由此可以得出,当R的位置下标 + L的位置下标 = 偶数时,会出现一个骨牌保持竖立的状态,该竖立骨牌的下标为(L的位置下标 - R的位置下标) / 2,即(7 - 1) / 2 = 3

  • 第 2 种可能:没有竖立的骨牌 image.png

在以上这种情况下,不存在竖立的骨牌,由此可以得出,当R的位置下标 + L的位置下标 = 奇数时,没有竖立的骨牌

第三种:左边与右边同为L或者R骨牌

image.png

由以上图片可以看出,不论左右两边同为L骨牌还是R骨牌,中间都不会出现竖立的骨牌

在知晓了以上三种情况之后,我们还需要对头部和尾部进行处理,即以下情况

  • 头部

image.png

通过以上图片, 我们可以看出,对于头部的连续竖立骨牌,当遇到R骨牌的时候,才会存在竖立的骨牌,此时竖立的骨牌数量为R的位置下标,即2

  • 尾部

image.png

其中n表示骨牌总数,通过以上图片,我们可以看出,对于头部的连续竖立骨牌,当遇到L骨牌的时候,才会存在竖立的骨牌,此时竖立的骨牌数量为骨牌总数 - L的下标位置 - 1,即n - (n - 3) - 1 = 2

解答思路

通过上面的题目分析,我们知道了所有可能状态,但是需要对全部都是.(即竖立状态)的情况进行特殊处理,以下是具体的解题思路:

  • 第一步:用一个vector<int> index;数组,存储LR的位置下标
  • 第二步:设置一个变量int sum = 0;存储竖立状态的总数,和一个vector<int> pos;数组,存储竖立状态的位置
  • 第三步:循环index数组,对RL``LR``RR``LL的情况进行判断,并更新总量sum和数组pos的值
  • 第四步:处理头部和尾部,并更新总量sum和数组pos的值
  • 第五步:对总量sum和数组pos进行处理,将其转换成符合格式要求的字符串格式
  • 最后不要忘记进行处理特殊情况,即没有RL,全部都是.的情况,直接返回结果

代码详解

接下来我们来逐步编写代码进行解析

  1. 用一个vector<int> index;数组,存储LR的位置下标
// 存储LR的下标
vector<int> index;
for(int i = 0; i < num; i++) {
    if(data[i] != '.') index.push_back(i);
}

2. 设置一个变量int sum = 0;存储竖立状态的总数,和一个vector<int> pos;数组,存储竖立状态的位置

int sum = 0; // 存储总量 
vector<int> pos; // 存储位置

3. 循环index数组,对RL``LR``RR``LL的情况进行判断,并更新总量sum和数组pos的值

for(int i = 1; i < index.size(); i++) {
    // LR => 全部都是平衡位置
    if(data[index[i - 1]] == 'L' && data[index[i]] == 'R') {
        sum += index[i] - index[i - 1] - 1;
        // 注意:最后返回的位置是从 1 开始计数的,所以我们要对下标进行 +1 操作
        for(int j = index[i - 1] + 1; j < index[i]; j++) pos.push_back(j + 1); 
    }
    // RL => 如果R+L是奇数,没有平衡位置,如果是偶数,有一个平衡位置
    else if(data[index[i - 1]] == 'R' && data[index[i]] == 'L') {
        if((index[i - 1] + index[i]) % 2 == 0) {
            sum++;
           pos.push_back((index[i - 1] + index[i]) / 2 + 1);
        }
    }
    // LL / RR => 0   index[i] == index[i - 1]

注意:pos存储的最后返回的位置是从 1 开始计数的,所以我们要对下标进行 +1 操作

  1. 处理头部和尾部,并更新总量sum和数组pos的值
 // 处理头部
if(data[index[0]] == 'R' && index[0] != 0) {
    sum += index[0];
    for(int i = 0; i < index[0]; i++) pos.push_back(i + 1) ;
}
// 处理尾部
if(data[index[index.size() - 1]] == 'L'  && index[index.size() - 1] != num - 1) {
    sum += num - index[index.size() - 1] - 1;
    for(int i = index[index.size() - 1] + 1; i < num; i++) pos.push_back(i + 1);
}

5. 对总量sum和数组pos进行处理,将其转换成符合格式要求的字符串格式

// 处理答案
string ans = "";
ans += to_string(sum);
if(sum != 0){
    ans += ':';
    sort(pos.begin(),pos.end()); // 注意:需要先排序一下
    for(int i = 0; i < pos.size(); i++) {
        if(i) ans += ',';
        ans += to_string(pos[i]);
    }
}

6. 最后,处理特殊情况,没有RL,全部都是.的情况,直接返回结果

// 特殊情况,没有RL
if(index.size() == 0){
    string ans = "";
    ans += to_string(num) + ':';
    for(int i = 0; i < data.size(); i++) {
        if(i) ans += ',';
        ans += to_string(i + 1);
    }
    return ans;
}

完整代码

以下给出C++过题完整代码

#include <bits/stdc++.h>
#include <string>
using namespace std;

string solution(int num, string data) {
    // Please write your code here
    // 存储LR的下标
    vector<int> index;
    for(int i = 0; i < num; i++) {
        if(data[i] != '.') index.push_back(i);
    }
    // 特殊情况,没有RL
    if(index.size() == 0){
        string ans = "";
        ans += to_string(num) + ':';
        for(int i = 0; i < data.size(); i++) {
            if(i) ans += ',';
            ans += to_string(i + 1);
        }
        return ans;
    }
    int sum = 0; // 存储总量
    vector<int> pos; // 存储位置
    for(int i = 1; i < index.size(); i++) {
        // LR => 全部都是平衡位置
        if(data[index[i - 1]] == 'L' && data[index[i]] == 'R') {
            sum += index[i] - index[i - 1] - 1;
            for(int j = index[i - 1] + 1; j < index[i]; j++) pos.push_back(j + 1);
        }
        // RL => 如果R+L是奇数,没有平衡位置,如果是偶数,有一个平衡位置
        else if(data[index[i - 1]] == 'R' && data[index[i]] == 'L') {
            if((index[i - 1] + index[i]) % 2 == 0) {
                sum++;
                pos.push_back((index[i - 1] + index[i]) / 2 + 1);
            }
        }
        // LL / RR => 0   index[i] == index[i - 1]
    }
    // 处理头部
    if(data[index[0]] == 'R' && index[0] != 0) {
        sum += index[0];
        for(int i = 0; i < index[0]; i++) pos.push_back(i + 1) ;
    }
    // 处理尾部
    if(data[index[index.size() - 1]] == 'L'  && index[index.size() - 1] != num - 1) {
        sum += num - index[index.size() - 1] - 1;
        for(int i = index[index.size() - 1] + 1; i < num; i++) pos.push_back(i + 1);
    }

    // 处理答案
    string ans = "";
    ans += to_string(sum);
    if(sum != 0){
        ans += ':';
        sort(pos.begin(),pos.end()); // 排序一下
        for(int i = 0; i < pos.size(); i++) {
            if(i) ans += ',';
            ans += to_string(pos[i]);
        }
    }

    return ans;
}

int main() {
    //  You can add more test cases here
    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;
}

个人思考与分析

在写这道题的时候,最开始的想法是两次循环,第一次先R倒,第二次L倒,最后处理情况,但是这种方式不能通过所有用例,其中有一个用例不过就是因为思路的局限性,最后采取了现在的这种方式,考虑到了所有的情况,但是我个人觉得还是过于复杂了,毕竟官方上这是一道简单题,所以不知道怎么样更好的优化,或者有别的更好的更简化的方式。

不过写这篇题解,也希望能给一样跟我久久都不知道怎么过这道题的伙伴们一点小帮助,如果有什么写的不好的或者不正确的也希望大家批评指正,非常感谢!