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