|
| 1 | +/* |
| 2 | + * Problem: Generate Parentheses (Medium) |
| 3 | + * Link: https://leetcode.com/problems/generate-parentheses/ |
| 4 | + * |
| 5 | + * The solution uses a backtracking approach to find all well-formed parantheses |
| 6 | + * pairs. |
| 7 | + */ |
| 8 | + |
| 9 | +#include <math.h> |
| 10 | +#include <stddef.h> |
| 11 | +#include <stdio.h> |
| 12 | +#include <stdlib.h> |
| 13 | +#include <string.h> |
| 14 | + |
| 15 | +/*---------------------------------------------------*/ |
| 16 | +// Things get easier to manage with this structure |
| 17 | +// It will essentially act a minimal version of `std::string`. |
| 18 | +// The length of the string is always fixed as we know what the length is going |
| 19 | +// to be. |
| 20 | +struct _Str |
| 21 | +{ |
| 22 | + char* buf; |
| 23 | + int len; |
| 24 | + int idx; |
| 25 | +} DefStr = { .idx = 0 }; |
| 26 | + |
| 27 | +// Doesn't add anything to the code other than convenience of writing `Str ...` |
| 28 | +// over `struct _Str ...` |
| 29 | +typedef struct _Str Str; |
| 30 | + |
| 31 | +// Pushes a character to the back of the Str |
| 32 | +void |
| 33 | +push_back(Str* self, char chr) |
| 34 | +{ |
| 35 | + if (self->idx > self->len) { |
| 36 | + // Length check |
| 37 | + return; |
| 38 | + } |
| 39 | + self->buf[self->idx++] = chr; |
| 40 | +} |
| 41 | + |
| 42 | +// Pops last character from the Str. Add Nullchar at the end just to signify the |
| 43 | +// end of string. |
| 44 | +void |
| 45 | +pop_back(Str* self) |
| 46 | +{ |
| 47 | + if (self->idx == 0) { |
| 48 | + return; |
| 49 | + } |
| 50 | + self->buf[self->idx--] = '\0'; |
| 51 | +} |
| 52 | + |
| 53 | +/*---------------------------------------------------*/ |
| 54 | + |
| 55 | +/* |
| 56 | + * This problem can be reduced to a subsets problem, where we explore every |
| 57 | + * possible parantheses combination and keep the valid ones. |
| 58 | + * |
| 59 | + * To do so, we will -- |
| 60 | + * 1. Generate a combination |
| 61 | + * 2. Check if it is balanced |
| 62 | + * 3. Backtrack |
| 63 | + * 4. Repeat |
| 64 | + */ |
| 65 | +void |
| 66 | +backtrack(char** results, |
| 67 | + int n, |
| 68 | + int open, |
| 69 | + int closed, |
| 70 | + Str* curr, |
| 71 | + int* returnSize) |
| 72 | +{ |
| 73 | + if (closed > open) { |
| 74 | + // This is the case when we prune the exploration of not well-formed |
| 75 | + // parentheses |
| 76 | + return; |
| 77 | + } |
| 78 | + if (open + closed == 2 * n) { |
| 79 | + // ^ checking lengths |
| 80 | + // Create a copy of the qualifying buffer and save it in Results |
| 81 | + // wf = well-formed |
| 82 | + results[*returnSize] = (char*)calloc(2 * n + 1, sizeof(char)); |
| 83 | + memcpy(results[*returnSize], curr->buf, 2 * n); |
| 84 | + *returnSize = *returnSize + 1; |
| 85 | + return; |
| 86 | + } |
| 87 | + |
| 88 | + if (open < n) { |
| 89 | + // If we do not have enough opening brackets, add one till we have `n` |
| 90 | + push_back(curr, '('); |
| 91 | + backtrack(results, n, open + 1, closed, curr, returnSize); |
| 92 | + |
| 93 | + // Pop this so that we can explore the other combinations |
| 94 | + pop_back(curr); |
| 95 | + } |
| 96 | + |
| 97 | + if (closed < n) { |
| 98 | + // If we do not have enough closing brackets, add one till we have `n` |
| 99 | + push_back(curr, ')'); |
| 100 | + backtrack(results, n, open, closed + 1, curr, returnSize); |
| 101 | + |
| 102 | + // Pop this so that we can explore the other combinations |
| 103 | + pop_back(curr); |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +/** |
| 108 | + * Note: The returned array must be malloced, assume caller calls free(). |
| 109 | + */ |
| 110 | +char** |
| 111 | +generateParenthesis(int n, int* returnSize) |
| 112 | +{ |
| 113 | + /* |
| 114 | + * Allocate enough memory for maximum possible combinations |
| 115 | + * The maximum would ideally be every possible combination (included invalid |
| 116 | + * ones). This is 2^(len of combination) == 2^(2 * n) |
| 117 | + * |
| 118 | + * Ofc, we will need less memory than this, but its fine. |
| 119 | + * |
| 120 | + * In the worst case according to our constraints, n = 9. |
| 121 | + * In this case, we will allocate 2^9 * 8 = 4096 bytes, which is 4KB. So we |
| 122 | + * can be sure that this program won't hog **too** much memory. |
| 123 | + */ |
| 124 | + |
| 125 | + char** results = (char**)malloc(pow(2, 2 * n) * sizeof(char*)); |
| 126 | + |
| 127 | + // Length is excluding null character, calloc includes null character |
| 128 | + // Calloc because it zeroes out our memory, so we automatically get a null |
| 129 | + // character. |
| 130 | + Str current = { .len = 2 * n, .buf = (char*)calloc(2 * n + 1, sizeof(char)) }; |
| 131 | + |
| 132 | + // We backtrack. |
| 133 | + *returnSize = 0; |
| 134 | + backtrack(results, n, 0, 0, ¤t, returnSize); |
| 135 | + |
| 136 | + // Free the Str buffer as we won't need it now. |
| 137 | + free(current.buf); |
| 138 | + |
| 139 | + // Return results |
| 140 | + return results; |
| 141 | +} |
0 commit comments