Leetcode 2337 -- 双指针&&脑筋急转弯
题目描述
在LR字符串中交换相邻字符
给定我们一个只由 \('L','R','X'\) 组成的字符串。现在给你两个字符串 \(start\) 和 \(end\),问你能否通过以下两个字符串变换,将字符串 \(start\) 变换为 \(end\)。
- \(XL \rightarrow LX\)
- \(RX \rightarrow XR\)
如果你观察仔细,你可以从上述两个变换发掘以下两个性质: - \(L\) 只能向左移动,并且左边的元素必须是 \(X\)
- \(R\) 只能向右移动,并且右边的元素必须是 \(X\)
思路1: bfs
\(TLE\)
暴力 \(bfs\) 的思路比较简单,暴力枚举所有交换即可。
但是超时也是意料之中的,不过我们可以增加一个优化,比如:A*, 剪枝(如果两个字符串的对应字符相同就不需要交换)等尽可能多得分。
思路2:双指针
首先,无论怎么移动,由于 \(L\) 和 \(R\) 无法互相穿过对方,那么去掉 \(X\) 后的剩余字符应该是相同的,否则返回 \(false\)。
然后,\(start\) 中 \(L\) 的位置一定在target中对应的 \(L\) 的右侧或者在同一位置
\(start\) 中 \(R\) 的位置一定在 \(target\) 中对应的 \(R\) 的左侧或者在同一位置(参见题目描述中的性质)
那么,接下来我们就用双指针遍历 \(start[i]\) 和 \(target[j]\),找到 \(start[i]\) 和 \(target[j]\) 都不位 'X' 的字符,判断它们的相对位置
分类讨论:
如果当前字符为 \(L\) 且 \(i<j\),那么这个 \(L\) 由于无法向右移动,返回 \(false\);
如果当前字符为 \(R\) 且 \(i>j\),那么这个 \(R\) 由于无法向左移动,返回 \(false\)。
遍历完,若中途没有返回 \(false\) 就返回 \(true\)。
双指针代码
class Solution {
public:
bool canChange(string start, string target) {
auto s = start, t = target;
// remove是将匹配的元素都放在后面,返回匹配的第一个元素的迭代器,然后erase删除迭代器
s.erase(remove(s.begin(), s.end(), '_'), s.end());
t.erase(remove(t.begin(), t.end(), '_'), t.end());
// 判断删除后长度和相对顺序,若不同则返回false
if (s != t) return false;
for (int i = 0, j = 0; i < start.length(); ++i) {
if (start[i] == '_') continue;
while (target[j] == '_') ++j;
// 若i=j,则说明一定匹配(因为在for循环上面的判断中保证了相对顺序)
// 若L:则它不能向右移动 i < j
// 若R:则它不能向左移动 j < i
if (i != j && (start[i] == 'L') == (i < j)) return false; // 一行判三种情况
++j;
}
return true;
}
};