|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Diagnostics; |
| 4 | +using System.Linq; |
| 5 | +using System.Text; |
| 6 | +using System.Threading.Tasks; |
| 7 | + |
| 8 | +namespace Leetcode301_removeInvalidParentheses |
| 9 | +{ |
| 10 | + class Program |
| 11 | + { |
| 12 | + /// <summary> |
| 13 | + /// Leetcode 301: remove invalid parentheses |
| 14 | + /// July 19, 2018 |
| 15 | + /// Study Leetcode discussion: |
| 16 | + /// https://leetcode.com/problems/remove-invalid-parentheses/discuss/75027/Easy-Short-Concise-and-Fast-Java-DFS-3-ms-solution |
| 17 | + /// |
| 18 | + /// Highlights of design: |
| 19 | + /// 1. Use recursive function |
| 20 | + /// 2. Find first unmatched ), and then remove all possible ) from the beginning; |
| 21 | + /// make recursive call; |
| 22 | + /// 3. After the unmatched close brackets has been removed, and then continue to |
| 23 | + /// process unmatched open brackets. Just call the reversed array. |
| 24 | + /// 4. At the end of the recursive function, only when the reversed string is processed, |
| 25 | + /// add the string to the valid string list; |
| 26 | + /// 5. Use two pointers left and right to scan through the string, find first char in every |
| 27 | + /// consecutive unmatched paretheses, add the option to remove it once. |
| 28 | + /// this is a nice and smart tip to remember, code from line 75 to 83. |
| 29 | + /// </summary> |
| 30 | + /// <param name="args"></param> |
| 31 | + static void Main(string[] args) |
| 32 | + { |
| 33 | + RunTestcase(); |
| 34 | + } |
| 35 | + |
| 36 | + static void RunTestcase() |
| 37 | + { |
| 38 | + var validStrings = RemoveInvalidParentheses("()())()"); |
| 39 | + |
| 40 | + Debug.Assert(validStrings[0].CompareTo("()()()") == 0 || |
| 41 | + validStrings[0].CompareTo("(())()") == 0); |
| 42 | + } |
| 43 | + |
| 44 | + /// <summary> |
| 45 | + /// |
| 46 | + /// </summary> |
| 47 | + /// <param name="source"></param> |
| 48 | + /// <returns></returns> |
| 49 | + public static List<String> RemoveInvalidParentheses(String source) |
| 50 | + { |
| 51 | + var validStrings = new List<String>(); |
| 52 | + |
| 53 | + var leftIndex = 0; |
| 54 | + var rightIndex = 0; |
| 55 | + var defaultLeftToRight = new char[] { '(', ')' }; |
| 56 | + |
| 57 | + removeInvalidCloseParenthesesTwoScans(source, validStrings, leftIndex, rightIndex, defaultLeftToRight); |
| 58 | + return validStrings; |
| 59 | + } |
| 60 | + |
| 61 | + /// <summary> |
| 62 | + /// code review July 19, 2018 |
| 63 | + /// remove invalid close parentheses two scans, |
| 64 | + /// one is from left to right and the second one is from right to left. |
| 65 | + /// |
| 66 | + /// go through the test case to figure out: |
| 67 | + /// "()())()" |
| 68 | + /// write a story how to find valid strings: |
| 69 | + /// 1. First remove extra close one. |
| 70 | + /// Find the first one unmatched close one, and then use two pointers |
| 71 | + /// to go over all possible places to remove close one. Remove close one. |
| 72 | + /// for example, ()())(), iterate the string from left to right, find index = 4, |
| 73 | + /// ) close parenthese is the extra one. What I like to do is to scan left to right |
| 74 | + /// to find all possible ), and then remove it. |
| 75 | + /// To make time complexity to linear, we can save close one index to a data structure, like a list. |
| 76 | + /// In case there are hundreds of chars, but only two or three close parentheses. |
| 77 | + /// |
| 78 | + /// |
| 79 | + /// Time complexity: |
| 80 | + /// Think about how many possible valid strings first. |
| 81 | + /// define close parentheses at most - M |
| 82 | + /// define open parentheses at most - N |
| 83 | + /// define unmatched close parentheses at most - M1 |
| 84 | + /// define unmatched open paretnthese at most - N1 |
| 85 | + /// possible choices: M ^ M1 * N ^N1 |
| 86 | + /// </summary> |
| 87 | + /// <param name="s"></param> |
| 88 | + /// <param name="validStrings"></param> |
| 89 | + /// <param name="right"></param> |
| 90 | + /// <param name="left"></param> |
| 91 | + /// <param name="parentheses"></param> |
| 92 | + private static void removeInvalidCloseParenthesesTwoScans( |
| 93 | + String s, |
| 94 | + List<String> validStrings, |
| 95 | + int right, |
| 96 | + int left, |
| 97 | + char[] parentheses) |
| 98 | + { |
| 99 | + var openOne = parentheses[0]; |
| 100 | + var closeOne = parentheses[1]; |
| 101 | + |
| 102 | + for (int openCount = 0, rightIndex = right; rightIndex < s.Length; ++rightIndex) |
| 103 | + { |
| 104 | + var current = s[rightIndex]; |
| 105 | + if (current == openOne) |
| 106 | + { |
| 107 | + openCount++; |
| 108 | + } |
| 109 | + |
| 110 | + if (current == closeOne) |
| 111 | + { |
| 112 | + openCount--; |
| 113 | + } |
| 114 | + |
| 115 | + if (openCount >= 0) |
| 116 | + { |
| 117 | + continue; |
| 118 | + } |
| 119 | + |
| 120 | + // find first unmatched parenthese, and then go over all the options to remove it. |
| 121 | + // for test case ()())(), close one has two positions, one is index = 1, index = 3 or 4 |
| 122 | + // remove index = 1, and index 3 and 4, only remove first one. Since the second one will be the same |
| 123 | + for (int leftIndex = left; leftIndex <= rightIndex; ++leftIndex) |
| 124 | + { |
| 125 | + if ( s[leftIndex] == closeOne && |
| 126 | + ( leftIndex == left || s[leftIndex - 1] != closeOne)) |
| 127 | + { |
| 128 | + // remove extra close one |
| 129 | + var stringFiltered = s.Substring(0, leftIndex) + s.Substring(leftIndex + 1); |
| 130 | + removeInvalidCloseParenthesesTwoScans(stringFiltered, validStrings, rightIndex, leftIndex, parentheses); |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + return; |
| 135 | + } |
| 136 | + |
| 137 | + |
| 138 | + var reversed = new String(s.ToCharArray().Reverse().ToArray()); |
| 139 | + |
| 140 | + if (parentheses[0] == '(') // finished left to right |
| 141 | + { |
| 142 | + removeInvalidCloseParenthesesTwoScans(reversed, validStrings, 0, 0, new char[] { ')', '(' }); |
| 143 | + } |
| 144 | + else // finished right to left |
| 145 | + { |
| 146 | + validStrings.Add(reversed); |
| 147 | + } |
| 148 | + } |
| 149 | + } |
| 150 | +} |
0 commit comments