diff --git a/strings/caesar_cipher.md b/strings/caesar_cipher.md new file mode 100644 index 0000000..5a6d153 --- /dev/null +++ b/strings/caesar_cipher.md @@ -0,0 +1,48 @@ +```python +"""Caecar Cipher implementation. + +Given a non-empty string of lowercase alphabetic characters, write a function that +takes in the string, and an integer called a key, +And returns a string obtained by shifting every letter of the input string by k positions +in the alphabet, where k is the key. + +Sample input: "xyz", 2 +Sample output: "zab" +""" + + +def caesar_cipher_encryptor(string, key): + """ + Complexity: + O(n) space + O(n) time + """ + new_letters = [] + new_key = key % 26 + for letter in string: + new_letter_code = ord(letter) + new_key + if new_letter_code > 122: # z character code is 122 + character = chr(96 + new_letter_code % 122) + new_letters.append(character) + else: + new_letters.append(chr(new_letter_code)) + + return "".join(new_letters) +``` + + +```python +caesar_cipher_encryptor("xyz", 2) +``` + + + + + 'zab' + + + + +```python + +``` diff --git a/strings/delimiter_order.md b/strings/delimiter_order.md new file mode 100644 index 0000000..1a5c38f --- /dev/null +++ b/strings/delimiter_order.md @@ -0,0 +1,72 @@ +```python +""" +Reverse the words in a string but maintain the relative order of delimiters. + +For example: + input = "hello:world/here" + output = "here:world/hello" + +""" +import re + + +def reverse(string, delimiters): + """Split the words and delimiters into their respective lists. + Then reverse the list of words and merge the two lists together. + + Complexity: O(n) time | O(n) space, where n == length of input string. + """ + words = re.split(f"[{delimiters}]+", string) + not_words = re.split(f"[^({delimiters})]+", string) + + # remove last empty string if present + if words[-1] == '': + words = words[:-1] + + # NOTE: we can in built reverse but there's always another way + # reversed_strings = list(reversed(words)) + start = 0 + end = len(words) - 1 + while start < end: + words[start], words[end] = words[end], words[start] + start += 1 + end -= 1 + + output = [] + for index, delimiter in enumerate(not_words): + print(index) + output.append(delimiter) + try: + print(f"adding {words[index]}") + output.append(words[index]) + except IndexError: + pass + + return ''.join(output) +``` + + +```python +reverse("hello:world/here", ":/") +``` + + 0 + adding here + 1 + adding world + 2 + adding hello + 3 + + + + + + 'here:world/hello' + + + + +```python + +``` diff --git a/strings/number_palindrome.md b/strings/number_palindrome.md new file mode 100644 index 0000000..d63b2d0 --- /dev/null +++ b/strings/number_palindrome.md @@ -0,0 +1,56 @@ +### Convert Number to palindrome +Given an integer, write a function that returns True if the number can be converted into a palindrome, otherwise return False. + +If the conversion steps result in a number bigger than 1 million, return False. + + +```python +def convertToPalindrome(num): + if isPalindrome(num): + return True + + while num < 1000000: + reverted = revert(num) + num = num + reverted + if isPalindrome(num): + return True, num + return False + +def revert(num): + """O(log10 (n) time -- since we are dividing by 10 every time. | O(1) space""" + reverted = 0 + while num > 0: + reverted = (reverted * 10 + num % 10) + num //= 10 + return reverted + +def isPalindrome(x): + if (x % 10 == 0 and x != 0) or x < 0: + return False + reverted = revert(x) + return x == reverted or x == reverted // 10 +``` + + +```python +convertToPalindrome(3335) +``` + + + + + (True, 8668) + + + + +```python +convertToPalindrome(95999) +``` + + + + + False + + diff --git a/strings/palindrome.md b/strings/palindrome.md new file mode 100644 index 0000000..302b6ab --- /dev/null +++ b/strings/palindrome.md @@ -0,0 +1,54 @@ +```python +""""All the ways to implement a palindrom checker. + +Check if the string is a palindrome, return True, otherwise, return False. + +A palindrome is defined as a string that is written the same forward and backward. +""" + + +def is_palindrome1(string): + """ + Complexity: + O(n) space + O(n) time + """ + reversed_char = [] + for i in reversed(range(len(string))): + reversed_char.append(string[i]) + return string == "".join(reversed_char) + + +def is_palindrome_best(string): + """ + Complexity: + O(1) space + O(n) time + """ + left_index = 0 + right_index = len(string) - 1 + + while left_index < right_index: + if string[left_index] != string[right_index]: + return False + left_index += 1 + right_index -= 1 + return True + + +def is_palindrome2(string, i = 0): + """ + Recursive implementation. + + Complexity: + O(n) space + O(n) time + """ + j = len(string) - 1 - i + return True if i >= j else string[i] == string[j] and is_palindrome2(string, i + 1) +``` + + +```python + +``` diff --git a/strings/parenthesis.md b/strings/parenthesis.md new file mode 100644 index 0000000..5ed94cb --- /dev/null +++ b/strings/parenthesis.md @@ -0,0 +1,57 @@ +## Count invalid parenthesis + +Given a string of parentheses, write a function to find the minimum number of parentheses to be removed to make the string valid (i.e. each open parenthesis is eventually closed). + +For example, given the string "()())()", you should return 1. Given the string ")(", you should return 2, since we must remove all of them. + +### Approach +For each opening parenthesis to be considered valid, they should eventually be matched with an closing parenthesis. If not, they are counted as invalid. +Whenever we encounter an unmatched closing parenthesis, we count as invalid. Eventually, we'll add the unmatched open count to the invalid one. + + +```python +def count_invalid_parenthesis(string) -> int: + invalid = 0 + opened = 0 + + for char in string: + if char == "(": + opened += 1 + elif char == ")": + if opened > 0: + opened -= 1 + else: + invalid += 1 + return invalid + opened +``` + + +```python +count_invalid_parenthesis("((()(())") +``` + + + + + 2 + + + + +```python +count_invalid_parenthesis("()()()") +``` + + + + + 0 + + + +This solution runs at O(N) time, where N = number of characters in the string. + + +```python + +``` diff --git a/strings/reverse_mutable_string.md b/strings/reverse_mutable_string.md new file mode 100644 index 0000000..e350c0e --- /dev/null +++ b/strings/reverse_mutable_string.md @@ -0,0 +1,56 @@ +```python +def reverse(array, start, end): + """Reverse characters in a string from start to end(inclusive).""" + while start < end: + array[start], array[end] = array[end], array[start] + start += 1 + end -= 1 + + +def reverse_mutable_string(string_list): + """ + This function performs a reverse operation on a mutable string in-place. + (saving on space) + Since strings are immutable, a mutable string can be represented + as a list of characters in a buffer e.g + ['h','e','l','l','o',' ','w','o','r','l','d']) == 'hello world' + + Approach: First, we reverse entire string to get 'dlrow olleh', + then we reverse each word within the string to obtain original words. + + Worst-case complexity: + Time: O(n) + Space: O(1) - since the input memory location is overwritten by the + output as we reverse the string as the algorithm executes. + """ + # reverse entire string + reverse(string_list, 0, len(string_list) - 1) + # reverse each word in the string + start = 0 + for end in range(len(string_list)): + if string_list[end] == " ": + reverse(string_list, start, end - 1) + start = end + 1 + + # reverse last word since we stopped at the last white space in the list + # remember, there are characters after the white space. + reverse(string_list, start, len(string_list) - 1) + return string_list +``` + + +```python +reverse_mutable_string(['h','e','l','l','o',' ','w','o','r','l','d']) +``` + + + + + ['w', 'o', 'r', 'l', 'd', ' ', 'h', 'e', 'l', 'l', 'o'] + + + + +```python + +``` diff --git a/strings/reverse_string_words.md b/strings/reverse_string_words.md new file mode 100644 index 0000000..8ee16a3 --- /dev/null +++ b/strings/reverse_string_words.md @@ -0,0 +1,46 @@ +## Problem + +Given a string of words delimited by spaces, reverse the words in string. For example, given "hello world here", return "here world hello" + +Follow-up: given a mutable string representation, can you perform this operation in-place? + +### Approach + +Since strings are immutable, a mutable string can be represented as a list of characters in a buffer e.g `['h','e','l','l','o',' ','w','o','r','l','d']` == 'hello world' + +First we reverse entire string to get `'dlrow olleh'`, then we reverse each word within the string to obtain original words. + + +```python +def reverse(string_list: list, start: int, end: int) -> str: + while start < end: + string_list[start], string_list[end] = string_list[end], string_list[start] + start += 1 + end -= 1 + +def reverse_words(string_list: list) -> str: + reverse(string_list, 0, len(string_list) - 1) + start = 0 + for i in range(len(string_list)): + if string_list[i] == " ": + reverse(string_list, start, i - 1) + start = i + 1 + # we need to reverse the last sub-string + reverse(string_list, start, len(string_list) - 1) + return "".join(string_list) +``` + + +```python +string_list = list("well hello there") +reverse_words(string_list) +``` + + + + + 'there hello well' + + + + diff --git a/strings/validate_ip_address.md b/strings/validate_ip_address.md new file mode 100644 index 0000000..e98ae37 --- /dev/null +++ b/strings/validate_ip_address.md @@ -0,0 +1,82 @@ +### Problem +Write a function to validate an IPv4 Address. + +An IPv4 addresses are canonically represented in dot-decimal notation, which consists of four decimal numbers, each ranging from 0 to 255, separated by dots, e.g., + +For example input `172.16.254.1` returns True, and `127...1` is invalid as well as `027.0.0.01` + + +```python +def valid_part(part): + n = len(part) + if n > 3: + return False + if part != '' and part[0] == 0 and n > 1: + return False + for c in part: + if c < "0" or c > "9": + return False + try: + num = int(part) + return num >= 0 and num <= 255 + except Exception as e: + return False + +def validate_ip(ip) -> bool: + count = 0 + for i in ip: + if i == '.': + count += 1 + if count != 3: + return False + # check for parts + parts = ip.split(".") + if len(parts) != 4: + return False + + for part in parts: + if not valid_part(part): + return False + return True +``` + + +```python +validate_ip("127.0.0.1") +``` + + + + + True + + + + +```python +validate_ip("1222.22.5.1") +``` + + + + + False + + + + +```python +validate_ip("1...1") +``` + + + + + False + + + + +```python + +```