LeetCode 777 - Swap Adjacent in LR String


https://leetcode.com/problems/swap-adjacent-in-lr-string/
In a string composed of 'L''R', and 'X' characters, like "RXXLRXRXL", a move consists of either replacing one occurrence of "XL" with "LX", or replacing one occurrence of "RX" with "XR". Given the starting string start and the ending string end, return True if and only if there exists a sequence of moves to transform one string to the other.
Example:
Input: start = "RXXLRXRXL", end = "XRLXXRRLX"
Output: True
Explanation:
We can transform start to end following these steps:
RXXLRXRXL ->
XRXLRXRXL ->
XRLXRXRXL ->
XRLXXRRXL ->
XRLXXRRLX
Note:
  1. 1 <= len(start) = len(end) <= 10000.
  2. Both start and end will only consist of characters in {'L', 'R', 'X'}.

http://www.programmersought.com/article/724530505/

IQ question

Originally thought to be a search problem similar to the maze, seeing the size of the data is so large, it does not feel like, look at the tag really is an IQ problem, then I gave up, IQ is not enough.
The topic says that you can change the XL in the initial string to LX. You can replace the RX in the initial string with XR, which means that the L in the initial string can only be moved to the left. In the initial string, R can only move to the right. And L and R are impossible to exchange the order, the number of L and R corresponding to the two strings should be equal. Once you know this rule, just use the double pointer to solve it.
Use i, j to point to the starting position of start and end respectively. If they point to X, then move to the right to a position other than X, then the characters they point to should be equal, otherwise return False. If it points to L, then the index of L in start must be smaller than the index of L in end; if it points to R, then the index of R in end must be larger than the index of R in end. It is necessary to move i and j backwards at the same time before the end of the while.
Returns True after all judgments are completed.
The time complexity is O(N) and the space complexity is O(1).
http://www.cnblogs.com/grandyang/p/9001474.html
这道题给了我们一个只含有L,R,X三个字符的字符串,然后说有两种操作,一种是把 "XL" 变成 "LX",另一种是把 "RX" 变成 "XR"。博主刚开始没有读题意,以为二者是可以互换的,错误的认为认为 "LX" 也能变成 "XL",其实题目这种变换是单向,这种单向关系就是解题的关键,具体来说,就是要把start字符串变成end字符串的话,L只能往前移动,因为是把 "XL" 变成 "LX",同样,R只能往后移动,因为是把 "RX" 变成 "XR"。题目给的那个例子并不能很好的说明问题,博主之前那种双向变换的错误认知会跪在这个例子:
start = "XXRXXLXXXX"
end  = "XXXXRXXLXX"
我们观察这个test case,可以发现start中的R可以往后移动,没有问题,但是start中的L永远无法变到end中L的位置,因为L只能往前移。这道题被归类为brainteaser,估计就是因为要观察出这个规律吧。那么搞明白这个以后,我们其实可以用双指针来解题,思路是,我们每次分别找到start和end中非X的字符,如果二者不相同的话,直接返回false,想想问什么?这是因为不论是L还是R,其只能跟X交换位置,L和R之间是不能改变相对顺序的,所以如果分别将start和end中所有的X去掉后的字符串不相等的话,那么就永远无法让start和end相等了。这个判断完之后,就来验证L只能前移,R只能后移这个限制条件吧,当i指向start中的L时,那么j指向end中的L必须要在前面,所以如果i小于j的话,就不对了,同理,当i指向start中的R,那么j指向end中的R必须在后面,所以i大于j就是错的,最后别忘了i和j同时要自增1,不然死循环了
    bool canTransform(string start, string end) {
        int n = start.size(), i = 0, j = 0;
        while (i < n && j < n) {
            while (i < n && start[i] == 'X') ++i;
            while (j < n && end[j] == 'X') ++j;
            if (start[i] != end[j]) return false;
            if ((start[i] == 'L' && i < j) || (start[i] == 'R' && i > j)) return false;
            ++i; ++j;
        }
        return true;
    }

https://xingxingpark.com/Leetcode-777-Swap-Adjacent-in-LR-String/
给出了两种变换的规则,从“XL”到“LX”和从“RX”到“XR”。所以我们可以给出两条规律:
  • 如果start能变换到end,那么除去两个字符串中的"X",剩余的字符串一定相同。因为任意"R""L"的相对顺序都不会发生变化,我们定义出去"X"的字符串为有效字符串
  • 根据变换的规则,"L"不能向右移,“R”不能向左移,所以 start 中“L”对应的 index "i" 一定不小于 end 中 “L”对应的index "j";start 中“R”对应的 index "i" 一定不大于 end 中 “R”对应的index "j"
    1. i >= j, 如果 start[i]==end[j]==”L”
    2. i <= j, 如果 start[i]==end[j]==”R”

X.
下面这种解法也挺巧妙的,这里我们使用两个计数器cntL和cntR,分别来统计L和R出现的次数,统计方法时,start中出现了L或R,计数器自增1,end中出现了L或R,计数器自减1。注意我们检测的顺序很重要,由于start中的R必须在end中的R前面,所以我们要先检测start中的R,同理,由于end中的L必须要在start中的L前面,所以我们要先检测end中的L,那么四个if写完后,如果cntL或者cntR中有任何一个小于0了,说明限制条件被打破了,返回false,或者当二者都大于0的时候,说明此时不匹配了,参见上面解法中对于去掉所有的X的解释,一个道理,说明L和R的相对顺序不同了,那么也是false。最终for循环退出后,如果cntL和cntR均为0的时候,才返回true,否则就是false
    bool canTransform(string start, string end) {
        int n = start.size(), cntL = 0, cntR = 0;
        for (int i = 0; i < n; ++i) {
            if (start[i] == 'R') ++cntR;
            if (end[i] == 'L') ++cntL;
            if (start[i] == 'L') --cntL;
            if (end[i] == 'R') --cntR;
            if (cntL < 0 || cntR < 0 || cntL * cntR != 0) return false;
        }
        return cntL == 0 && cntR == 0;
    }

https://medium.com/@ChYuan/leetcode-no-777-swap-adjacent-in-lr-string-%E5%BF%83%E5%BE%97-medium-748be5d19c36
一開始我以為是互換都可以,覺得很困難,但是再仔細看題目,其實是只有兩種換的方式,再好好的分析,會發現其實可以不用管 "X",把換的方式想成 L 只能往左移,R 只能往右移 (並且碰到 L or R 就不能再移動了)。

如果要合法,就有四個要求要做到:
(1) 全部字串長度相等
(2) 去掉 X 後的字串要相等
(3) 因為 L只能往左移,所以原本字串和後來字串的 L 關係就是原本字串的 L 一定不能比後來字串的 L 早出現。
(4) R 以此類推

X. Stack
我覺得這種題目,要是跟你說 swap 規則一定不是用 swap 去解
一開始真的嘗試用暴力法(BFS)去遞迴,馬上就超過時間限制
    public boolean canTransform(String start, String end) {
        ArrayList<Character> array = new ArrayList<Character>();
        
        start = start.replaceAll("\"", "").replace("\\", "").replace("\\", "");
        end   = end.replaceAll("\"", "").replace("\\", "").replace("\\", "");

        String rl_start = start.replaceAll("X", "");
        String rl_end  = end.replaceAll("X", "");
        
        if (!rl_start.equals(rl_end)) { return false; }
        if (start.length() != end.length()) { return false; }
        
        Stack<Character> stack = new Stack<Character>();
        for (int i = 0; i < start.length(); i++) {
            if (start.charAt(i) == 'R') { stack.push(start.charAt(i)); }
            if (end.charAt(i) == 'R') { 
                if (stack.empty()) {
                    return false;
                } else {
                    stack.pop();
                }
            }
        }
        
        stack = new Stack<Character>();
        for (int i = 0; i < start.length(); i++) {
            if (end.charAt(i) == 'L') { stack.push(end.charAt(i)); }
            if (start.charAt(i) == 'L') { 
                if (stack.empty()) {
                    return false;
                } else {
                    stack.pop();
                }
            }
        }
        
        return true;
    }

从左向右遍历start和end,在遍历过程中,start中的字符R的个数应当不小于end中字符R的个数(保证R没有发生相对左移)

从右向左遍历start和end,在遍历过程中,start中的字符L的个数应当不小于end中字符L的个数(保证L没有发生相对右移)

将start和end中的字符'X'移除,用'R'分割两字符串得到的新字符串应相等。
def canTransform(self, start, end): """ :type start: str :type end: str :rtype: bool """ N = len(start) sR = eR = sL = eL = 0 for x in range(N): if start[x] == 'R': sR += 1 if end[x] == 'R': eR += 1 if sR < eR: return False if sR != eR: return False for x in range(N - 1, -1, -1): if start[x] == 'L': sL += 1 if end[x] == 'L': eL += 1 if sL < eL: return False if sL != eL: return False start, end = start.replace('X', ''), end.replace('X', '') return start.split('R') == end.split('R')
X. https://leetcode.com/problems/swap-adjacent-in-lr-string/discuss/113782/Python-simple-solution-3-lines-O(n)
The idea it to compare 'L' position in start is greater than or equal to that in end, also 'R' position in start is less than or equal to that in end.
The reason is 'L' can move left when 'X' exists before, and 'R' can move right when 'X' exists after, and 'L' and 'R' cannot cross each other, so we do not care about 'X'.
class Solution:
    def canTransform(self, start, end):
        """
        :type start: str
        :type end: str
        :rtype: bool
        """
        s = [(c, i) for i, c in enumerate(start) if c == 'L' or c == 'R']
        e = [(c, i) for i, c in enumerate(end) if c == 'L' or c == 'R']
        return len(s) == len(e) and all(c1 == c2 and (i1 >= i2 and c1 == 'L' or i1 <= i2 and c1 == 'R') for (c1, i1), (c2, i2) in zip(s,e))

X. Proof
https://www.1point3acres.com/bbs/thread-431706-1-1.html
推论1:除去X后,R和L的相对位置在初始和最终状态不变。
推论2:L只能向左移动,所以最终状态L的index,必须<=初始时L的index
             R只能向右移动,所以最终状态R的index,必须>=初始时R的index
推论3:初始和结束字符串长度相等


怎么证明推论1,2,3是 Start可以move到End的充分条件?也就是=>反过来也成立?

This shows being solid and accessible are necessary conditions. With an induction on the number of people, we can show that they are sufficient conditions. The basic idea of the proof is this: A person on the end either walks away from the others, or walks towards. If they walk away, then let them walk first and it is true; if they walk towards, then let them walk last and it is true (by virtue of the target being solid).
可以用数学归纳法进行构造性证明,首先明确要证明的内容:
定理1:对于任意长度的两个字符串A和B,如果推论1/2/3同时成立,那么存在从A到B的移动序列S。

(这里所定义的移动序列里面每个元素都是“将第m个L左移(R右移)一格”这种格式。)

现在,给定推论1/2/3成立,不妨设字符串中L/R的总数量为n,我们对n进行归纳:

Base case:
n = 1,易证。

Inductive step:
设当n = k时,定理1成立。

考虑n = k + 1,我们先做如下操作:将A和B各自最左边一个非X字符替换为X,记替换后的字符串分别为A',B'。
易证A'和B'满足推论1/2/3,由归纳假设可知存在A'到B'的移动序列,记为S'。

现在我们来构造A到B的移动序列S,考虑所替换的最左边的字符,两种情况:
Case 1, L
首先移动A中最左边的L到B中最左边L的位置,易证这一移动序列是可行的(因为是将A的最左边的字符向左移动)。然后,对A应用移动序列S',易证S'不会触碰到最左边的L,所以也是可行的。

Case 2, R
首先对A应用变换序列S',同样易证S'不会触碰到最左边的R,然后移动A中最左边的R到B中最左边R的位置。

这样我们就构造出了一个可行的移动序列S,也就证明了定理1对任意n都是成立的。
(而且这个证明其实就是上面那段英文所说的内容。)

如果假设开始时没有L或者R,都是X。假设End字符串已知,反过来想怎么一步步在满足3个条件的情况下,插入L和R使得形成的Start字符串可以移动到End。

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts