Last active
July 21, 2016 00:35
-
-
Save jacobwalkr/58f166a9e8dc3816b3412e726b56989e to your computer and use it in GitHub Desktop.
C solution for Simple Text Editor on HackerRank
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#define APPEND 1 | |
#define DELETE 2 | |
#define PRINT 3 | |
#define UNDO 4 | |
#define MAX_INPUT 1000000 | |
/** | |
* Exits with an error if ptr is null | |
*/ | |
void ensure(void *ptr) { | |
if (ptr == NULL) { | |
printf("Memory allocation failed :("); | |
exit(EXIT_FAILURE); | |
} | |
} | |
// Need to declare action_t beforehand. Might as well do both! | |
typedef struct action_t action_t; | |
typedef struct state_t state_t; | |
/** | |
* An action describes an APPEND or DELETE operation. *deleted holds the characters that were | |
* removed by a DELETE operation. word_length holds the length of characters either added or | |
* deleted. operation is either APPEND or DELETE. *previous holds a reference to the next action | |
* in the reverse-chronological linked list. | |
*/ | |
struct action_t { | |
char *deleted; | |
int word_length; | |
int operation; | |
action_t *previous; | |
}; | |
/** | |
* The "top" of the linked list. Maintains a reference to the list and a count of the items in it | |
*/ | |
struct state_t { | |
char *string; | |
int string_length; | |
action_t *last_action; | |
}; | |
/** | |
* Add to the string - returns the number of characters added | |
*/ | |
int Append(state_t *state, const char *word) { | |
int word_length = strlen(word); | |
// Add space for new string | |
state->string = realloc(state->string, | |
(sizeof(char) * (strlen(state->string) + word_length + 1))); | |
ensure(state->string); | |
// Copy the new string in (includes null terminator) | |
strcpy(&(state->string[strlen(state->string)]), word); | |
return word_length; | |
} | |
/** | |
* Delete from the string - returns the string deleted | |
*/ | |
char *Delete(state_t *state, int count) { | |
// Copy out deleted chars - leave space for the null terminator! | |
char *deleted_string = malloc(sizeof(char) * (count + 1)); | |
int delete_from = strlen(state->string) - count; | |
strncpy(deleted_string, &(state->string[delete_from]), count + 1); | |
// Shrink state string and replace null terminator | |
state->string = realloc(state->string, strlen(state->string)); | |
ensure(state->string); | |
state->string[delete_from] = '\0'; | |
return deleted_string; | |
} | |
/** | |
* Utility function to wrap strtol and return an int | |
*/ | |
int ParseIntArg(char *arg) { | |
char *endptr; | |
return strtol(arg, &endptr, 10); | |
} | |
/** | |
* Perform an APPEND or DELETE action and update the history | |
*/ | |
void DoStringAction(state_t *state, int operation, char *argument) { | |
// Create a history action | |
action_t *new_action = (action_t *)malloc(sizeof(action_t)); | |
// Identify the argument and perform specific actions | |
if (operation == APPEND) { | |
int word_length = Append(state, argument); | |
new_action->deleted = NULL; | |
new_action->word_length = word_length; | |
} | |
else if (operation == DELETE) { | |
char *deleted_string = Delete(state, ParseIntArg(argument)); | |
new_action->deleted = deleted_string; | |
new_action->word_length = strlen(deleted_string); | |
} | |
// Finish up the history action | |
new_action->operation = operation; | |
new_action->previous = state->last_action; | |
// Update history | |
state->last_action = new_action; // already referenced previous head | |
} | |
/** | |
* Drop the latest addition to the history - frees memory and updates the "top" of the list | |
*/ | |
void Undo(state_t *state) { | |
// Is there anything to undo? | |
if (state->last_action == NULL) { | |
return; | |
} | |
// Reverse the latest action | |
if (state->last_action->operation == APPEND) { | |
Delete(state, state->last_action->word_length); | |
} | |
else if (state->last_action->operation == DELETE) { | |
Append(state, state->last_action->deleted); | |
} | |
// Preserve the old top to free | |
action_t *old_top = state->last_action; | |
// Move the new top into place | |
state->last_action = state->last_action->previous; | |
// Drop the old top | |
free(old_top); | |
} | |
/** | |
* Loop through inputs (number given on first line), parsing numerical instruction and argument | |
* | |
* Undo history stored in a linked list of "states" | |
*/ | |
int main() { | |
// Stack for instructions | |
state_t *state = malloc(sizeof(state_t)); | |
state->string = (char *)malloc('\0'); | |
unsigned int input_lines; | |
scanf("%u", &input_lines); | |
getchar(); // Need to consume the remaining newline | |
for (unsigned int i = 0; i < input_lines; i += 1) { | |
// Read input line | |
char input[MAX_INPUT + 3]; // max total length of all input strings + \0 + int | |
fgets(input, sizeof(input), stdin); | |
// Parse the operation first, then deal with the rest accordingly | |
char *after_operation; | |
int operation = strtol(input, &after_operation, 10); | |
if (operation == APPEND || operation == DELETE) { | |
// The argument is everything after the operation and a space | |
char *argument = malloc(strlen(after_operation)); | |
sscanf(after_operation, " %s", argument); | |
DoStringAction(state, operation, argument); | |
} | |
else if (operation == PRINT) { | |
// The argument is a number! Print nth character, 1-indexed | |
int argument; | |
sscanf(after_operation, " %d", &argument); | |
printf("%c\n", state->string[argument - 1]); | |
} | |
else { | |
// Must be UNDO. No argument. | |
Undo(state); | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment