added rip

This commit is contained in:
Rui Ribeiro 2025-09-22 17:26:39 +01:00
parent 393bcc3028
commit 8e4fd06731
4 changed files with 479 additions and 0 deletions

92
rip/Makefile Normal file
View File

@ -0,0 +1,92 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# Makefile :+: :+: :+: #
# +:+ +:+ +:+ #
# By: ruiferna <ruiferna@student.42porto.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# 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

179
rip/README.md Normal file
View File

@ -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!

180
rip/rip.c Normal file
View File

@ -0,0 +1,180 @@
#include <unistd.h>
#include <stdlib.h>
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, &current->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);
}

28
rip/subject.txt Normal file
View File

@ -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
(() ) $
( )( ) $
( ) () $
()( ) $