diff --git a/rip/Makefile b/rip/Makefile new file mode 100644 index 0000000..dca5ecb --- /dev/null +++ b/rip/Makefile @@ -0,0 +1,92 @@ +# **************************************************************************** # +# # +# ::: :::::::: # +# Makefile :+: :+: :+: # +# +:+ +:+ +:+ # +# By: ruiferna +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2025/09/22 00:00:00 by ruiferna #+# #+# # +# Updated: 2025/09/22 17:26:07 by ruiferna ### ########.fr # +# # +# **************************************************************************** # + +# Program name +NAME = rip + +# Compiler and flags +CC = gcc +CFLAGS = -Wall -Wextra -Werror + +# Source files +SRCS = rip.c + +# Object files +OBJS = $(SRCS:.c=.o) + +# Colors for output +GREEN = \033[0;32m +RED = \033[0;31m +YELLOW = \033[0;33m +NC = \033[0m # No Color + +# Default target +all: $(NAME) + +# Build the executable +$(NAME): $(OBJS) + @echo "$(YELLOW)Linking $(NAME)...$(NC)" + @$(CC) $(CFLAGS) $(OBJS) -o $(NAME) + @echo "$(GREEN)โœ… $(NAME) created successfully!$(NC)" + +# Compile source files to object files +%.o: %.c + @echo "$(YELLOW)Compiling $<...$(NC)" + @$(CC) $(CFLAGS) -c $< -o $@ + +# Clean object files +clean: + @echo "$(RED)๐Ÿงน Cleaning object files...$(NC)" + @rm -f $(OBJS) + @echo "$(GREEN)โœ… Object files cleaned!$(NC)" + +# Clean object files and executable +fclean: clean + @echo "$(RED)๐Ÿงน Cleaning executable...$(NC)" + @rm -f $(NAME) + @echo "$(GREEN)โœ… Everything cleaned!$(NC)" + +# Rebuild everything +re: fclean all + +# Test the program with provided examples +test: $(NAME) + @echo "$(YELLOW)๐Ÿงช Running tests...$(NC)" + @echo "$(YELLOW)Test 1: '(()'$(NC)" + @./$(NAME) '(((' | cat -e + @echo "$(YELLOW)Test 2: '((()()())())'$(NC)" + @./$(NAME) '((()()())())' | cat -e + @echo "$(YELLOW)Test 3: '()())()''$(NC)" + @./$(NAME) '()())()' | cat -e + @echo "$(YELLOW)Test 4: '(()(()('$(NC)" + @./$(NAME) '(()(()(' | cat -e + @echo "$(GREEN)โœ… All tests completed!$(NC)" + +# Check for memory leaks (requires valgrind) +valgrind: $(NAME) + @echo "$(YELLOW)๐Ÿ” Checking for memory leaks...$(NC)" + @valgrind --leak-check=full --show-leak-kinds=all ./$(NAME) '(()' + @echo "$(GREEN)โœ… Memory check completed!$(NC)" + +# Show help +help: + @echo "$(GREEN)Available targets:$(NC)" + @echo " $(YELLOW)all$(NC) - Build the program" + @echo " $(YELLOW)clean$(NC) - Remove object files" + @echo " $(YELLOW)fclean$(NC) - Remove object files and executable" + @echo " $(YELLOW)re$(NC) - Rebuild everything (fclean + all)" + @echo " $(YELLOW)test$(NC) - Run all test cases" + @echo " $(YELLOW)valgrind$(NC) - Check for memory leaks" + @echo " $(YELLOW)help$(NC) - Show this help message" + +# Declare phony targets +.PHONY: all clean fclean re test valgrind help \ No newline at end of file diff --git a/rip/README.md b/rip/README.md new file mode 100644 index 0000000..84671e4 --- /dev/null +++ b/rip/README.md @@ -0,0 +1,179 @@ +# The Parentheses Balancing Problem (RIP Exercise) + +## What is this exercise about? + +Imagine you have a string of parentheses like this: `"(()"`. + +Think of parentheses like **doors** in a building: +- `(` is like **opening a door** +- `)` is like **closing a door** + +For everything to work properly, every door you open must have a matching door that closes it! + +## The Problem + +Sometimes we get strings where the doors don't match properly. For example: +- `"((("` - We opened 3 doors but never closed any! +- `"())"` - We opened 1 door, closed 1 door, but then tried to close another door that was never opened! + +Our job is to **fix** these strings by removing the minimum number of parentheses (but replacing them with spaces instead of deleting them completely). + +## Real-World Example + +Let's say you have: `"(()"` + +This means: +1. Open door 1: `(` +2. Open door 2: `(` +3. Close door 2: `)` +4. But door 1 is still open! ๐Ÿ˜ฑ + +To fix this, we need to either: +- Remove one of the opening doors: ` ()` (remove first `(`) +- Or remove one of the opening doors: `( )` (remove second `(`) + +Both solutions work! + +## How Our Program Works + +### Step 1: Count the Problems + +```c +// This function counts how many parentheses we need to remove +void calculate_removals(char *str, int *left_rem, int *right_rem) +{ + int left = 0; // Count of unmatched opening parentheses + int right = 0; // Count of unmatched closing parentheses + + // Go through each character + while (str[i]) + { + if (str[i] == '(') + left++; // Found an opening door + else if (str[i] == ')') + { + if (left > 0) + left--; // Found a closing door for an open one + else + right++; // Found a closing door with no matching open door + } + } +} +``` + +**Example with `"(()"`:** +- Start: `left = 0`, `right = 0` +- See `(`: `left = 1` (one unmatched opening) +- See `(`: `left = 2` (two unmatched openings) +- See `)`: `left = 1` (one opening got matched) +- End: `left = 1`, `right = 0` + +So we need to remove **1 opening parenthesis**. + +### Step 2: Try All Possible Solutions + +Think of it like trying different combinations: + +```c +// This function tries removing different parentheses to find all valid solutions +void solve(char *s, int pos, int left_rem, int right_rem, int open, char *path) +{ + // If we've looked at all characters + if (pos == len) + { + // Check if we removed exactly what we needed and everything balances + if (left_rem == 0 && right_rem == 0 && open == 0) + add_result(results, path); // This is a valid solution! + return; + } + + // Try removing this parenthesis (replace with space) + if (s[pos] == '(' && left_rem > 0) + { + path[pos] = ' '; // Replace with space + solve(s, pos + 1, left_rem - 1, right_rem, open, path); + } + + // Try keeping this parenthesis + path[pos] = s[pos]; + if (s[pos] == '(') + solve(s, pos + 1, left_rem, right_rem, open + 1, path); + // ... and so on +} +``` + +### Step 3: Avoid Duplicates + +We use a list to store all unique solutions: + +```c +// This makes sure we don't print the same solution twice +void add_result(t_result **results, char *str) +{ + // Check if we already have this solution + current = *results; + while (current) + { + if (ft_strcmp(current->str, str) == 0) + return; // Already have it, don't add again + current = current->next; + } + + // It's new, so add it to our list + new->str = ft_strdup(str); + new->next = *results; + *results = new; +} +``` + +## Example Walkthrough + +Let's trace through `"(()"` step by step: + +### Initial Analysis: +- Need to remove 1 opening parenthesis (`left_rem = 1`) +- Need to remove 0 closing parentheses (`right_rem = 0`) + +### Trying Different Positions: + +**Position 0 (first `(`):** +- Try removing it: `" ()"` + - Check: removed 1 opening โœ“, no unmatched doors โœ“ + - **Valid solution!** โœ… + +**Position 1 (second `(`):** +- Try removing it: `"( )"` + - Check: removed 1 opening โœ“, no unmatched doors โœ“ + - **Valid solution!** โœ… + +**Position 2 (the `)`):** +- Can't remove it (we need to remove openings, not closings) + +### Final Output: +``` + () +( ) +``` + +## More Complex Example: `"()())()"` + +This string has: +- 3 opening doors: `(`, `(`, `(` +- 4 closing doors: `)`, `)`, `)`, `)` + +So we have 1 extra closing door that needs to be removed. + +**All possible solutions:** +- Remove closing at position 2: `"()( )()"` +- Remove closing at position 3: `"()() ()"` +- Remove closing at position 4: `"( ())()"` + +## Why This Exercise is Useful + +This problem teaches us: +1. **Problem-solving**: Break down complex problems into smaller steps +2. **Recursion**: Try all possibilities systematically +3. **Data structures**: Use lists to store and manage results +4. **Optimization**: Find the minimum changes needed + +It's like being a **door inspector** - you need to make sure every building (string) has properly matched doors (parentheses) with the minimum number of changes! \ No newline at end of file diff --git a/rip/rip.c b/rip/rip.c new file mode 100644 index 0000000..960ae96 --- /dev/null +++ b/rip/rip.c @@ -0,0 +1,180 @@ +#include +#include + +typedef struct s_result +{ + char *str; + struct s_result *next; +} t_result; + +int ft_strlen(char *str) +{ + int len; + + len = 0; + while (str[len]) + len++; + return (len); +} + +int ft_strcmp(char *s1, char *s2) +{ + int i; + + i = 0; + while (s1[i] && s2[i] && s1[i] == s2[i]) + i++; + return (s1[i] - s2[i]); +} + +char *ft_strdup(char *src) +{ + char *dup; + int len; + int i; + + len = ft_strlen(src); + dup = malloc(len + 1); + if (!dup) + return (NULL); + i = 0; + while (i < len) + { + dup[i] = src[i]; + i++; + } + dup[len] = '\0'; + return (dup); +} + +void add_result(t_result **results, char *str) +{ + t_result *new; + t_result *current; + + current = *results; + while (current) + { + if (ft_strcmp(current->str, str) == 0) + return ; + current = current->next; + } + new = malloc(sizeof(t_result)); + if (!new) + return ; + new->str = ft_strdup(str); + if (!new->str) + { + free(new); + return ; + } + new->next = *results; + *results = new; +} + +void print_results(t_result *results) +{ + t_result *current; + int i; + + current = results; + while (current) + { + i = 0; + while (current->str[i]) + { + write(1, ¤t->str[i], 1); + i++; + } + write(1, "\n", 1); + current = current->next; + } +} + +void free_results(t_result *results) +{ + t_result *temp; + + while (results) + { + temp = results; + results = results->next; + free(temp->str); + free(temp); + } +} + +void solve(char *s, int pos, int left_rem, int right_rem, int open, char *path, t_result **results) +{ + int len; + + len = ft_strlen(s); + if (pos == len) + { + if (left_rem == 0 && right_rem == 0 && open == 0) + add_result(results, path); + return ; + } + if (s[pos] == '(' && left_rem > 0) + { + path[pos] = ' '; + solve(s, pos + 1, left_rem - 1, right_rem, open, path, results); + } + if (s[pos] == ')' && right_rem > 0) + { + path[pos] = ' '; + solve(s, pos + 1, left_rem, right_rem - 1, open, path, results); + } + path[pos] = s[pos]; + if (s[pos] == '(') + solve(s, pos + 1, left_rem, right_rem, open + 1, path, results); + else if (s[pos] == ')' && open > 0) + solve(s, pos + 1, left_rem, right_rem, open - 1, path, results); + else + solve(s, pos + 1, left_rem, right_rem, open, path, results); +} + +void remove_invalid_parentheses(char *s) +{ + int left_rem; + int right_rem; + int len; + char *path; + int i; + t_result *results; + + left_rem = 0; + right_rem = 0; + len = ft_strlen(s); + results = NULL; + i = 0; + while (s[i]) + { + if (s[i] == '(') + left_rem++; + else if (s[i] == ')') + { + if (left_rem > 0) + left_rem--; + else + right_rem++; + } + i++; + } + path = malloc(len + 1); + if (!path) + return ; + path[len] = '\0'; + solve(s, 0, left_rem, right_rem, 0, path, &results); + print_results(results); + free_results(results); + free(path); +} + +int main(int argc, char **argv) +{ + if (argc != 2) + return (1); + remove_invalid_parentheses(argv[1]); + return (0); +} \ No newline at end of file diff --git a/rip/subject.txt b/rip/subject.txt new file mode 100644 index 0000000..0958bd5 --- /dev/null +++ b/rip/subject.txt @@ -0,0 +1,28 @@ +Assignment name : rip +Expected files : *.c *.h +Allowed functions: puts, write +-------------------------------------------------------------------------------- + +Write a program that will take as argument a string containing only parenthesis. +if the parenthesis are unbalanced (for example "())") +your program shall remove the minimum number of parentheses for the expression to be balanced. +By removing we mean replacing by spaces. +You will then print all the solutions (can be more than one). + +The order of the solutions is not important. + +For example this should work: +$> ./rip '(()' | cat -e + ()$ +( )$ +$> ./rip '((()()())())' | cat -e +((()()())())$ +$> ./rip '()())()'| cat -e +()() ()$ +()( )()$ +( ())()$ +$> ./rip '(()(()(' | cat -e +(() ) $ +( )( ) $ +( ) () $ + ()( ) $ \ No newline at end of file