added rip
This commit is contained in:
parent
393bcc3028
commit
8e4fd06731
92
rip/Makefile
Normal file
92
rip/Makefile
Normal 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
179
rip/README.md
Normal 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
180
rip/rip.c
Normal 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, ¤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);
|
||||||
|
}
|
||||||
28
rip/subject.txt
Normal file
28
rip/subject.txt
Normal 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
|
||||||
|
(() ) $
|
||||||
|
( )( ) $
|
||||||
|
( ) () $
|
||||||
|
()( ) $
|
||||||
Loading…
Reference in New Issue
Block a user