Taro Logo

Encode and Decode TinyURL

Medium
1 views
2 months ago

TinyURL is a URL shortening service where you enter a URL such as https://leetcode.com/problems/design-tinyurl and it returns a short URL such as http://tinyurl.com/4e9iAk. Design a class to encode a URL and decode a tiny URL.

There is no restriction on how your encode/decode algorithm should work. You just need to ensure that a URL can be encoded to a tiny URL and the tiny URL can be decoded to the original URL.

Implement the Solution class:

  • Solution() Initializes the object of the system.
  • String encode(String longUrl) Returns a tiny URL for the given longUrl.
  • String decode(String shortUrl) Returns the original long URL for the given shortUrl. It is guaranteed that the given shortUrl was encoded by the same object.

For example:

Input: url = "https://leetcode.com/problems/design-tinyurl"
Output: "https://leetcode.com/problems/design-tinyurl"

Explanation:
Solution obj = new Solution();
string tiny = obj.encode(url); // returns the encoded tiny url.
string ans = obj.decode(tiny); // returns the original url after decoding it.
Sample Answer
class Solution:
    def __init__(self):
        self.url_map = {}
        self.id = 0
        self.base = "http://tinyurl.com/"

    def encode(self, longUrl: str) -> str:
        """Encodes a URL to a shortened URL.
        """
        self.id += 1
        shortUrl = self.base + str(self.id)
        self.url_map[shortUrl] = longUrl
        return shortUrl

    def decode(self, shortUrl: str) -> str:
        """Decodes a shortened URL to its original URL.
        """
        return self.url_map[shortUrl]


# Your Solution object will be instantiated and called as such:
# solution = Solution()
# solution.decode(solution.encode(url))

Naive Solution

The provided solution is already a fairly straightforward and efficient approach. A more "naive" solution might involve generating random strings until a unique short URL is found that isn't already in the url_map. However, this could lead to collisions and would be less efficient than the current solution.

Optimal Solution

The given solution is already relatively optimal for the problem constraints. It uses a simple incrementing ID to generate short URLs, which avoids collisions and is easy to implement. The use of a dictionary (url_map) provides fast lookups for decoding.

Big(O) Run-time Analysis

  • encode(longUrl): O(1)
    • Incrementing the ID is a constant-time operation.
    • Creating the short URL string is a constant-time operation.
    • Storing the mapping in the dictionary url_map is a constant-time operation (on average).
  • decode(shortUrl): O(1)
    • Looking up the shortUrl in the url_map dictionary is a constant-time operation (on average).

Big(O) Space Usage Analysis

  • The space complexity is O(n), where n is the number of URLs encoded.
    • The url_map dictionary stores all the encoded URLs, so the space used grows linearly with the number of URLs.
    • Each URL (both long and short) takes up space proportional to its length.

Edge Cases and Handling

  1. Very Long URLs: The code handles long URLs without any specific limitations, as Python strings can accommodate very large strings.

  2. URL Collisions (Hypothetical): The incrementing ID ensures that short URLs will always be unique, preventing collisions.

  3. Invalid Short URLs: If an invalid shortUrl (i.e., not present in url_map) is passed to decode(), it will raise a KeyError. The code could be modified to return None or raise a custom exception in this case for more robust error handling.

    def decode(self, shortUrl: str) -> str:
        try:
            return self.url_map[shortUrl]
        except KeyError:
            return None  # Or raise a custom exception
    
  4. Base URL modification: If the base URL is modified after encoding URLs, the decode function will be invalid. To avoid this, the base URL should be stored in a configuration file and not modified after initialization.