字符串题目:在LR字符串中交换相邻字符

题目

标题和出处

标题:在LR字符串中交换相邻字符

出处:777. 在LR字符串中交换相邻字符

难度

5 级

题目描述

要求

在一个由 ‘L’ \texttt{`L'} ‘L’ ‘R’ \texttt{`R'} ‘R’ ‘X’ \texttt{`X'} ‘X’ 三个字符组成的字符串(例如 "RXXLRXRXL" \texttt{"RXXLRXRXL"} "RXXLRXRXL")中进行移动操作。一次移动操作指用一个 "LX" \texttt{"LX"} "LX" 替换一个 "XL" \texttt{"XL"} "XL",或者用一个 "XR" \texttt{"XR"} "XR" 替换一个 "RX" \texttt{"RX"} "RX"。现给定起始字符串 start \texttt{start} start 和结束字符串 end \texttt{end} end,当且仅当存在一系列移动操作使得 start \texttt{start} start 可以转换成 end \texttt{end} end 时,返回 true \texttt{true} true

示例

示例 1:

输入: start   =   "RXXLRXRXL",   end   =   "XRLXXRRLX" \texttt{start = "RXXLRXRXL", end = "XRLXXRRLX"} start = "RXXLRXRXL", end = "XRLXXRRLX"
输出: true \texttt{true} true
解释:我们可以通过以下几步将 start \texttt{start} start 转换成 end \texttt{end} end
RXXLRXRXL → \texttt{RXXLRXRXL} \rightarrow RXXLRXRXL
XRXLRXRXL → \texttt{XRXLRXRXL} \rightarrow XRXLRXRXL
XRLXRXRXL → \texttt{XRLXRXRXL} \rightarrow XRLXRXRXL
XRLXXRRXL → \texttt{XRLXXRRXL} \rightarrow XRLXXRRXL
XRLXXRRLX \texttt{XRLXXRRLX} XRLXXRRLX

示例 2:

输入: start   =   "X",   end   =   "L" \texttt{start = "X", end = "L"} start = "X", end = "L"
输出: false \texttt{false} false

示例 3:

输入: start   =   "LLR",   end   =   "RRL" \texttt{start = "LLR", end = "RRL"} start = "LLR", end = "RRL"
输出: false \texttt{false} false

示例 4:

输入: start   =   "XL",   end   =   "LX" \texttt{start = "XL", end = "LX"} start = "XL", end = "LX"
输出: true \texttt{true} true

示例 5:

输入: start   =   "XLLR",   end   =   "LXLX" \texttt{start = "XLLR", end = "LXLX"} start = "XLLR", end = "LXLX"
输出: false \texttt{false} false

数据范围

  • 1 ≤ start.length ≤ 10 4 \texttt{1} \le \texttt{start.length} \le \texttt{10}^\texttt{4} 1start.length104
  • start.length = end.length \texttt{start.length} = \texttt{end.length} start.length=end.length
  • start \texttt{start} start end \texttt{end} end 中的字符仅限于 ‘L’ \texttt{`L'} ‘L’ ‘R’ \texttt{`R'} ‘R’ ‘X’ \texttt{`X'} ‘X’

解法

思路和算法

一次移动操作用一个 “LX" \text{``LX"} “LX" 替换一个 “XL" \text{``XL"} “XL",或者用一个 “XR" \text{``XR"} “XR" 替换一个 “RX" \text{``RX"} “RX",等价于以下描述:

  • 如果字符串中的一个 ‘L’ \text{`L'} ‘L’ 的左边是 ‘X’ \text{`X'} ‘X’,则将这个 ‘L’ \text{`L'} ‘L’ 向左移动一步,这个 ‘L’ \text{`L'} ‘L’ 左边的 ‘X’ \text{`X'} ‘X’ 向右移动一步;

  • 如果字符串中的一个 ‘R’ \text{`R'} ‘R’ 的右边是 ‘X’ \text{`X'} ‘X’,则将这个 ‘R’ \text{`R'} ‘R’ 向右移动一步,这个 ‘R’ \text{`R'} ‘R’ 右边的 ‘X’ \text{`X'} ‘X’ 向左移动一步。

分析上述移动操作,可以得到如下结论:

  1. 移动操作只会交换字符串中两个相邻的字符,不会改变字符串中的每种字符的数量;

  2. 每次移动操作交换的两个相邻字符中必须有一个 ‘X’ \text{`X'} ‘X’,不可能将一个 ‘L’ \text{`L'} ‘L’ 和一个 ‘R’ \text{`R'} ‘R’ 交换,因此在任意次数的移动操作之后,字符串中的 ‘L’ \text{`L'} ‘L’ ‘R’ \text{`R'} ‘R’ 的相对顺序不会改变;

  3. 每次移动操作一定是将 ‘L’ \text{`L'} ‘L’ 向左移动或者将 ‘R’ \text{`R'} ‘R’ 向右移动,因此在任意次数的移动操作之后,每个 ‘L’ \text{`L'} ‘L’ 的位置一定是不变或左移,每个 ‘R’ \text{`R'} ‘R’ 的位置一定是不变或右移,不可能出现 ‘L’ \text{`L'} ‘L’ 的位置右移或者 ‘R’ \text{`R'} ‘R’ 的位置左移的情况。

基于上述结论,可以遍历字符串 start \textit{start} start end \textit{end} end,判断是否存在一系列移动操作使得 start \textit{start} start 可以转换成 end \textit{end} end

根据结论 1 和结论 2 可知,当 start \textit{start} start 可以转换成 end \textit{end} end 时,如果分别将字符串 start \textit{start} start end \textit{end} end 中的全部字符 ‘X’ \text{`X'} ‘X’ 去掉,只保留字符 ‘L’ \text{`L'} ‘L’ ‘R’ \text{`R'} ‘R’,则得到的两个子序列是相等的。因此,同时遍历字符串 start \textit{start} start end \textit{end} end,遍历的过程中跳过字符 ‘X’ \text{`X'} ‘X’,比较两个字符串对应位置的字符(由于跳过字符 ‘X’ \text{`X'} ‘X’,因此可以保证比较的字符都不是 ‘X’ \text{`X'} ‘X’),如果对应位置的字符不同,则与结论 1 和结论 2 矛盾,返回 false \text{false} false。如果其中的一个字符串已经遍历到末尾,则另一个字符串剩下的字符必须都是 ‘X’ \text{`X'} ‘X’,否则与结论 1 和结论 2 矛盾,返回 false \text{false} false

对于结论 3 的判断,需要比较两个字符串对应位置的字符的下标,以下用 index 1 \textit{index}_1 index1 表示 start \textit{start} start 中的下标, index 2 \textit{index}_2 index2 表示 end \textit{end} end 中的下标。当字符是 ‘L’ \text{`L'} ‘L’ 时,应满足 index 1 ≥ index 2 \textit{index}_1 \ge \textit{index}_2 index1index2;当字符是 ‘R’ \text{`R'} ‘R’ 时,应满足 index 1 ≤ index 2 \textit{index}_1 \le \textit{index}_2 index1index2。如果存在对应位置的字符的下标不满足上述条件,则与结论 3 矛盾,返回 false \text{false} false

如果上述三个结论都满足,则一定存在一系列移动操作使得 start \textit{start} start 可以转换成 end \textit{end} end,返回 true \text{true} true

代码

class Solution {
    public boolean canTransform(String start, String end) {
        int length = start.length();
        int index1 = 0, index2 = 0;
        while (index1 < length && index2 < length) {
            while (index1 < length && start.charAt(index1) == 'X') {
                index1++;
            }
            while (index2 < length && end.charAt(index2) == 'X') {
                index2++;
            }
            if (index1 < length && index2 < length) {
                char c1 = start.charAt(index1), c2 = end.charAt(index2);
                if (c1 != c2) {
                    return false;
                }
                if (c1 == 'L' && index1 < index2 || c1 == 'R' && index1 > index2) {
                    return false;
                }
                index1++;
                index2++;
            }
        }
        while (index1 < length) {
            if (start.charAt(index1) != 'X') {
                return false;
            }
            index1++;
        }
        while (index2 < length) {
            if (end.charAt(index2) != 'X') {
                return false;
            }
            index2++;
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 start \textit{start} start end \textit{end} end 的长度。需要遍历字符串 start \textit{start} start end \textit{end} end 各一次。

  • 空间复杂度: O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值