题目
标题和出处
标题:在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} 1≤start.length≤104
- 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’ 向左移动一步。
分析上述移动操作,可以得到如下结论:
-
移动操作只会交换字符串中两个相邻的字符,不会改变字符串中的每种字符的数量;
-
每次移动操作交换的两个相邻字符中必须有一个 ‘X’ \text{`X'} ‘X’,不可能将一个 ‘L’ \text{`L'} ‘L’ 和一个 ‘R’ \text{`R'} ‘R’ 交换,因此在任意次数的移动操作之后,字符串中的 ‘L’ \text{`L'} ‘L’ 和 ‘R’ \text{`R'} ‘R’ 的相对顺序不会改变;
-
每次移动操作一定是将 ‘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 index1≥index2;当字符是 ‘R’ \text{`R'} ‘R’ 时,应满足 index 1 ≤ index 2 \textit{index}_1 \le \textit{index}_2 index1≤index2。如果存在对应位置的字符的下标不满足上述条件,则与结论 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)。