【C/C++】2337. 移动片段得到字符串

197 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情


题目链接:2337. 移动片段得到字符串

题目描述

给你两个字符串 starttarget ,长度均为 n 。每个字符串 由字符 'L''R''_' 组成,其中:

  • 字符 'L''R' 表示片段,其中片段 'L' 只有在其左侧直接存在一个 空位 时才能向 移动,而片段 'R' 只有在其右侧直接存在一个 空位 时才能向 移动。
  • 字符 '_' 表示可以被 任意 'L''R' 片段占据的空位。

如果在移动字符串 start 中的片段任意次之后可以得到字符串 target ,返回 true ;否则,返回 false

提示:

  • n == start.length == target.length
  • 1n1051 \leqslant n \leqslant 10^5
  • starttarget 由字符 'L''R''_' 组成

示例 1:

输入:start = "_L__R__R_", target = "L______RR"
输出:true
解释:可以从字符串 start 获得 target ,需要进行下面的移动:
- 将第一个片段向左移动一步,字符串现在变为 "L___R__R_" 。
- 将最后一个片段向右移动一步,字符串现在变为 "L___R___R" 。
- 将第二个片段向右移动散步,字符串现在变为 "L______RR" 。
可以从字符串 start 得到 target ,所以返回 true

示例 2:

输入:start = "R_L_", target = "__LR"
输出:false
解释:字符串 start 中的 'R' 片段可以向右移动一步得到 "_RL_" 。
但是,在这一步之后,不存在可以移动的片段,所以无法从字符串 start 得到 target 。

示例 2:

输入:start = "_R", target = "R_"
输出:false
解释:字符串 start 中的片段只能向右移动,所以无法从字符串 start 得到 target 。

整理题意

题目给定两个长度相同的字符串 starttarget,字符串中的字符仅包含 LR_

  • L 只能往左移动,且只能在 _ 上移动,也就是只能向左与 _ 交换位置;
  • R 只能往右移动,且只能在 _ 上移动,也就是只能向右与 _ 交换位置;
  • _ 表示可以被 任意 'L''R' 片段占据的空位。

start 是否能够通过上述规则移动字符变成 target

解题思路分析

由于字符只能在 _ 上进行移动,不能穿越其他字符,所以我们可以首先判断 starttarget 每个字符相对位置是否一致,如果相对位置都不一致,那么 start 无论如何移动都无法变成 target

由于 L 只能往左移动,R 只能往右移动,那么 starttarget 中一一对应的字符存在一下关系:

  • targetL 的位置一定在其对应 start 中位置的左边,这是因为 start 中的 L 只能往左移动的原因;
  • 同理 targetR 的位置一定在其对应 start 中位置的右边。

具体实现

  1. 首先将两个字符串去除 _ 字符后进行比较是否一致,这也是确保两个字符串中的 LR 能够一一对应的基本条件。
  2. 再遍历 start 中的每个字符,在 target 中找到其对应的字符,判断当前字符在两个字符串中的位置关系是否合理,我们用 i 表示 start 中字符的位置,j 表示 target 中对应字符的位置:
    • 如果当前字符是 L:那么 j <= i 是满足条件的,j > i 是不符合条件的;
    • 如果当前字符是 R:那么 j >= i 是满足条件的,j < i 是不符合条件的;
  3. 遇到不符合条件的情况直接返回 false
  4. 如果所有对应字符都符合情况,那么返回 true

复杂度分析

  • 时间复杂度:O(n)O(n)n 为字符串长度,需要遍历两个字符串各两次。
  • 空间复杂度:O(1)O(1),可以优化为 O(1)O(1) 的空间复杂度,在代码实现中为了方便创建了两个字符串进行存储。

代码实现

class Solution {
public:
    bool canChange(string start, string target) {
        int n = start.length();
        //首先判断除去下划线后所有字符顺序是否相等
        string s, t;
        s = t = "";
        for(int i = 0; i < n; i++){
            if(start[i] != '_') s += start[i];
            if(target[i] != '_') t += target[i];
        }
        if(s != t) return false;
        for(int i = 0, j = 0; i < n; i++){
            //找到start字符串中的字符
            if(start[i] == '_') continue;
            //在找到target中一一对应的字符
            while(target[j] == '_') j++;
            //判断位置关系
            //if(start[i] == 'L' && j > i) return false;
            //if(start[i] == 'R' && j < i) return false;
            //上面两句可以写成一句
            if((i != j) && ((start[i] == 'L') != j < i)) return false;
            j++;
        }
        return true;
    }
};

总结

  • 该题核心思想为 一一对应,在一一对应的基础上再判断相对位置关系即可。
  • 因为一一对应,所以可以 贪心 的进行比较对应字符的位置关系即可。
  • 在代码实现中将两句判断语句简化成了一句,这是因为一个交叉关系的原因,首先排除 i != j 的情况,那么 if(start[i] == 'L' && j > i) 中的两个判断条件和 if(start[i] == 'R' && j < i) 中的两个判断条件可以进行交叉判断写成 (i != j) && ((start[i] == 'L') != j < i) 进行简化代码,但实际上只是一个骚操作,对程序的复杂度没有任何影响,在大多数情况下写成易懂的代码更优。
  • 测试结果:

2337.png

结束语

有些人看似努力,实际都是在做表面功夫,浪费了时间,也麻痹了自己。有些人不声不响,努力做好每一件事,日积月累,最终获得想要的结果。真正的努力不是敷衍应付,而是认准目标后持之以恒地付出。新的一天,加油!