在问 · 算法题解

535. TinyURL 的加密与解密

中等设计自增 ID + 62 进制力扣原题 ↗

题目描述

实现一个网址缩短服务的类 Codec(部分语言中叫 Solution),它能把一个很长的网址换成一个简短的链接(比如以 http://tinyurl.com/ 开头的形式),也能把短链接还原回来。

需要实现的方法:构造函数创建一个空的服务对象;encode(longUrl) 接收一个长网址,返回为它生成的短网址;decode(shortUrl) 接收一个短网址,返回它对应的原始长网址。

具体的编码规则由你决定,唯一的要求是对任意合法网址 url,decode(encode(url)) 必须得到原来的 url。传给 decode 的短网址保证由同一个对象的 encode 生成。

示例 1
输入:url = "https://leetcode.com/problems/design-tinyurl"
输出:"https://leetcode.com/problems/design-tinyurl"

解释:先调用 encode 得到短网址,再对它调用 decode,结果就是原网址。

数据范围

  • 1 <= url.length <= 10^4
  • 保证 url 是合法的网址。

解题思路:自增 ID + 62 进制

用自增计数器给每个长链接发一个编号 id,把 id 转成 62 进制字符串(0-9a-zA-Z)作为短码,并在哈希表 db 中记下 短码 → 长链接。encode 返回 前缀 + 短码;decode 截掉前缀得到短码,再查 db。

为什么正确

id 只增不减,所以发出的短码两两不同;db 中每个短码恰好对应一个长链接,decode 查到的一定是 encode 时存入的那个。

复杂度分析

encode 需转换 O(log62 id) 位,decode 截取短码 O(L)、查表 O(1);db 存所有链接,空间 O(总长度)。

易错点

短码应基于计数器或检查冲突,直接取哈希截断可能两个链接撞到同一个短码;JS/TS 的 db 若写在函数外,多个测试用例会共享,id 必须一直递增而不是每次从头开始,否则旧短码会被覆盖。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

class Codec:
    def __init__(self):
        self.db = {}
        self.id = 0

    def encode(self, longUrl: str) -> str:
        self.id += 1
        n, code = self.id, ''
        while n > 0:
            code = CHARS[n % 62] + code
            n //= 62
        self.db[code] = longUrl
        return 'http://tinyurl.com/' + code

    def decode(self, shortUrl: str) -> str:
        code = shortUrl[len('http://tinyurl.com/'):]
        return self.db[code]

载入编辑器,自己跑一遍 →

其他解法

自增 ID 保证不冲突且短码最短,但会暴露生成顺序;随机短码长度固定、不可预测,需要冲突检测。L 为网址长度。

解法 2:随机生成短码 时间 O(L),空间 O(L)

每次编码时从 62 个字符中随机取 6 个拼成短码。如果短码已经在表中,就重新生成。确认唯一后把短码映射到长网址。解码时直接按短码查表。

短码不可预测,别人无法按顺序猜出其他链接。代价是要处理冲突,表越满重试越多;6 位 62 进制约有 5.7×10¹⁰ 种取值,实际几乎不冲突。

import random

CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

class Codec:
    def __init__(self):
        self.db = {}

    def encode(self, longUrl: str) -> str:
        while True:
            code = ''.join(random.choice(CHARS) for _ in range(6))
            if code not in self.db:
                break
        self.db[code] = longUrl
        return 'http://tinyurl.com/' + code

    def decode(self, shortUrl: str) -> str:
        code = shortUrl[len('http://tinyurl.com/'):]
        return self.db[code]

设计 专题的其他题目

查看 设计 全部题目 →