Skip to main content

Command Palette

Search for a command to run...

Solving Leetcode Interviews in Seconds with AI: Encrypt and Decrypt Strings

Updated
4 min read

Introduction

In this blog post, we will explore how to solve the LeetCode problem "2227" using AI. LeetCode is a popular platform for preparing for coding interviews, and with the help of AI tools like Chatmagic, we can generate solutions quickly and efficiently - helping you pass the interviews and get the job offer without having to study for months.

Problem Statement

You are given a character array keys containing unique characters and a string array values containing strings of length 2. You are also given another string array dictionary that contains all permitted original strings after decryption. You should implement a data structure that can encrypt or decrypt a 0-indexed string. A string is encrypted with the following process: For each character c in the string, we find the index i satisfying keys[i] == c in keys. Replace c with values[i] in the string. Note that in case a character of the string is not present in keys, the encryption process cannot be carried out, and an empty string "" is returned. A string is decrypted with the following process: For each substring s of length 2 occurring at an even index in the string, we find an i such that values[i] == s. If there are multiple valid i, we choose any one of them. This means a string could have multiple possible strings it can decrypt to. Replace s with keys[i] in the string. Implement the Encrypter class: Encrypter(char[] keys, String[] values, String[] dictionary) Initializes the Encrypter class with keys, values, and dictionary. String encrypt(String word1) Encrypts word1 with the encryption process described above and returns the encrypted string. int decrypt(String word2) Returns the number of possible strings word2 could decrypt to that also appear in dictionary. Example 1: Input ["Encrypter", "encrypt", "decrypt"] [[['a', 'b', 'c', 'd'], ["ei", "zf", "ei", "am"], ["abcd", "acbd", "adbc", "badc", "dacb", "cadb", "cbda", "abad"]], ["abcd"], ["eizfeiam"]] Output [null, "eizfeiam", 2] Explanation Encrypter encrypter = new Encrypter([['a', 'b', 'c', 'd'], ["ei", "zf", "ei", "am"], ["abcd", "acbd", "adbc", "badc", "dacb", "cadb", "cbda", "abad"]); encrypter.encrypt("abcd"); // return "eizfeiam". // 'a' maps to "ei", 'b' maps to "zf", 'c' maps to "ei", and 'd' maps to "am". encrypter.decrypt("eizfeiam"); // return 2. // "ei" can map to 'a' or 'c', "zf" maps to 'b', and "am" maps to 'd'. // Thus, the possible strings after decryption are "abad", "cbad", "abcd", and "cbcd". // 2 of those strings, "abad" and "abcd", appear in dictionary, so the answer is 2. Constraints: 1 <= keys.length == values.length <= 26 values[i].length == 2 1 <= dictionary.length <= 100 1 <= dictionary[i].length <= 100 All keys[i] and dictionary[i] are unique. 1 <= word1.length <= 2000 2 <= word2.length <= 200 All word1[i] appear in keys. word2.length is even. keys, values[i], dictionary[i], word1, and word2 only contain lowercase English letters. At most 200 calls will be made to encrypt and decrypt in total.

Explanation

Here's a breakdown of the approach, followed by the Python code:

  • Encryption: Use a dictionary to map characters in keys to their corresponding 2-character strings in values for efficient encryption.

  • Decryption: Build a reverse mapping from 2-character strings in values to corresponding characters in keys. Then, recursively explore possible decryptions, checking if the resulting strings are present in the dictionary using a set for fast lookup.

  • Optimization: Use a set for the dictionary, which provides O(1) average time complexity for checking if a string is present.

  • Complexity:

    • Runtime Complexity: Encryption: O(n), where n is the length of word1. Decryption: O(2^(m/2)), where m is the length of word2, in the worst case due to the number of possible decryptions. In practice, it will likely be much faster, especially given the dictionary constraints and the relatively short strings involved.
    • Storage Complexity: O(k + d + v), where k is the length of keys, d is the total length of all strings in the dictionary, and v is the length of values.

Code

    class Encrypter:

    def __init__(self, keys: list[str], values: list[str], dictionary: list[str]):
        self.key_to_val = {}
        self.val_to_keys = {}
        self.dictionary = set(dictionary)
        self.keys = keys
        self.values = values

        for i in range(len(keys)):
            self.key_to_val[keys[i]] = values[i]
            if values[i] not in self.val_to_keys:
                self.val_to_keys[values[i]] = []
            self.val_to_keys[values[i]].append(keys[i])


    def encrypt(self, word1: str) -> str:
        encrypted = ""
        for char in word1:
            if char not in self.key_to_val:
                return ""
            encrypted += self.key_to_val[char]
        return encrypted

    def decrypt(self, word2: str) -> int:
        self.count = 0
        self.word2 = word2
        self.decryptions = set()
        self.decrypt_recursive(0, "")
        return self.count

    def decrypt_recursive(self, index, current_string):
        if index == len(self.word2):
            if current_string in self.dictionary and current_string not in self.decryptions:
                self.count += 1
                self.decryptions.add(current_string)
            return

        substring = self.word2[index:index+2]
        if substring in self.val_to_keys:
            for key in self.val_to_keys[substring]:
                self.decrypt_recursive(index + 2, current_string + key)

More from this blog

C

Chatmagic blog

2894 posts