LeetCode838. 推多米诺—开学第一天的LeetCode每日一题= =

一、双指针算法

  发现受力总可以分为一个一个的区间,这个区间的中间是不受力的'.',两边是力。
  若区间形如 [ 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;
    }
};

二、宽度优先搜索

  本题类似一个扩散过程,注意到如果一个牌子同一时刻只受一个力的影响,则会倾倒;
  所以可以维护 t i m e time time数组表示牌子确定受力的时间,一个队列表示已经受力了正在等待处理的牌子的下标, f o r c e force force数组用来存储每个牌子的受力情况。
  每次处理队头元素时,若其受力数仅为1,则把它倾倒,即把 r e t [ q . f r o n t ( ) ] ret[q.front()] ret[q.front()]改为对应方向,然后看看它会往哪边倾倒,然后考虑它倾倒方向的牌子 n e i d x neidx neidx
  若 n e i d x neidx neidx还未被受力影响过,那么把它的受力时间更新为time[q.front()] + 1,并且把它的受力情况更新到force[neidx]中,然后把它入队列待处理,
  若 n e i d x neidx neidx的上次受力时间就等于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;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值