携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情
题目链接:2337. 移动片段得到字符串
题目描述
给你两个字符串 start 和 target ,长度均为 n 。每个字符串 仅 由字符 'L'、'R' 和 '_' 组成,其中:
- 字符
'L'和'R'表示片段,其中片段'L'只有在其左侧直接存在一个 空位 时才能向 左 移动,而片段'R'只有在其右侧直接存在一个 空位 时才能向 右 移动。 - 字符
'_'表示可以被 任意'L'或'R'片段占据的空位。
如果在移动字符串 start 中的片段任意次之后可以得到字符串 target ,返回 true ;否则,返回 false 。
提示:
n == start.length == target.lengthstart和target由字符'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 。
整理题意
题目给定两个长度相同的字符串 start 和 target,字符串中的字符仅包含 L、R 和 _:
L只能往左移动,且只能在_上移动,也就是只能向左与_交换位置;R只能往右移动,且只能在_上移动,也就是只能向右与_交换位置;_表示可以被 任意'L'或'R'片段占据的空位。
问 start 是否能够通过上述规则移动字符变成 target。
解题思路分析
由于字符只能在 _ 上进行移动,不能穿越其他字符,所以我们可以首先判断 start 和 target 每个字符相对位置是否一致,如果相对位置都不一致,那么 start 无论如何移动都无法变成 target。
由于 L 只能往左移动,R 只能往右移动,那么 start 和 target 中一一对应的字符存在一下关系:
target中L的位置一定在其对应start中位置的左边,这是因为start中的L只能往左移动的原因;- 同理
target中R的位置一定在其对应start中位置的右边。
具体实现
- 首先将两个字符串去除
_字符后进行比较是否一致,这也是确保两个字符串中的L和R能够一一对应的基本条件。 - 再遍历
start中的每个字符,在target中找到其对应的字符,判断当前字符在两个字符串中的位置关系是否合理,我们用i表示start中字符的位置,j表示target中对应字符的位置:- 如果当前字符是
L:那么j <= i是满足条件的,j > i是不符合条件的; - 如果当前字符是
R:那么j >= i是满足条件的,j < i是不符合条件的;
- 如果当前字符是
- 遇到不符合条件的情况直接返回
false; - 如果所有对应字符都符合情况,那么返回
true。
复杂度分析
- 时间复杂度:,
n为字符串长度,需要遍历两个字符串各两次。 - 空间复杂度:,可以优化为 的空间复杂度,在代码实现中为了方便创建了两个字符串进行存储。
代码实现
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)进行简化代码,但实际上只是一个骚操作,对程序的复杂度没有任何影响,在大多数情况下写成易懂的代码更优。 - 测试结果:
结束语
有些人看似努力,实际都是在做表面功夫,浪费了时间,也麻痹了自己。有些人不声不响,努力做好每一件事,日积月累,最终获得想要的结果。真正的努力不是敷衍应付,而是认准目标后持之以恒地付出。新的一天,加油!