LeetCode 535. TinyURL 的加密与解密 题解。
535. TinyURL 的加密与解密#
https://leetcode.cn/problems/encode-and-decode-tinyurl/
TinyURL 是一种 URL 简化服务, 比如:当你输入一个 URL https://leetcode.com/problems/design-tinyurl 时,它将返回一个简化的URL http://tinyurl.com/4e9iAk 。请你设计一个类来加密与解密 TinyURL 。
加密和解密算法如何设计和运作是没有限制的,你只需要保证一个 URL 可以被加密成一个 TinyURL ,并且这个 TinyURL 可以用解密方法恢复成原本的 URL 。
实现 Solution 类:
- Solution() 初始化 TinyURL 系统对象。
- String encode(String longUrl) 返回 longUrl 对应的 TinyURL 。
- String decode(String shortUrl) 返回 shortUrl 原本的 URL 。题目数据保证给定的 shortUrl 是由同一个系统对象加密的。
自增id#
这是后端常用的手段,使用自增id来表示一个编码,也唯一标识一条数据。
自增id在不考虑安全性的情况下,可以作为实现成本较低的方案。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
/**
* 执行用时:
* 1 ms
* , 在所有 Java 提交中击败了
* 73.55%
* 的用户
* 内存消耗:
* 41.5 MB
* , 在所有 Java 提交中击败了
* 59.23%
* 的用户
* 通过测试用例:
* 739 / 739
* <p>
* 自增id
*/
class AutoIncrementIdCodec {
int id;
Map<String, String> short2Long;
/**
* 短链服务域名前缀
*/
String prefix = "https://redolog.github.io/";
public AutoIncrementIdCodec() {
id = 0;
short2Long = new HashMap<>();
}
// Encodes a URL to a shortened URL.
public String encode(String longUrl) {
++id;
String shortUrl = String.valueOf(id);
short2Long.put(shortUrl, longUrl);
return prefix + shortUrl;
}
// Decodes a shortened URL to its original URL.
public String decode(String shortUrl) {
return short2Long.get(shortUrl.substring(shortUrl.lastIndexOf("/") + 1));
}
}
|
复杂度#
n:入参字符串长度
encode
:只需id递增,将长短映射放入map
,O(1)
。
decode
:需遍历短链、截取,O(n)
。
映射占用链长级别的空间,占用O(n)
。
随机串拼接#
使用
1
|
baseStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
作为字符素材源,拼接固定长度的组合,一共有len(baseStr)^codeLen
种排列,并且每个位置的字符可重复,整体编码不重复即可。
我们使用持久化的方式校验重复。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
/**
* 执行用时:
* 3 ms
* , 在所有 Java 提交中击败了
* 54.45%
* 的用户
* 内存消耗:
* 41.7 MB
* , 在所有 Java 提交中击败了
* 27.36%
* 的用户
* 通过测试用例:
* 739 / 739
*/
class RandomKeyCodec {
/**
* 概率随机
*/
ThreadLocalRandom random;
/**
* 生成素材串
*/
String baseStr;
/**
* 存储映射
*/
Map<String, String> short2Long;
/**
* 短链后缀长度
*/
int codeLen;
/**
* 短链服务域名前缀
*/
String prefix = "https://dragonsong.tech/";
public RandomKeyCodec() {
random = ThreadLocalRandom.current();
baseStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
short2Long = new HashMap<>();
codeLen = 8;
}
// Encodes a URL to a shortened URL.
public String encode(String longUrl) {
String shortCode;
do {
shortCode = gen(longUrl);
} while (short2Long.containsKey(shortCode));
short2Long.put(shortCode, longUrl);
return prefix + shortCode;
}
private String gen(String longUrl) {
char[] codeArr = new char[codeLen];
for (int i = 0; i < codeLen; i++) {
codeArr[i] = baseStr.charAt(random.nextInt(baseStr.length()));
}
return String.valueOf(codeArr);
}
// Decodes a shortened URL to its original URL.
public String decode(String shortUrl) {
return short2Long.get(shortUrl.substring(shortUrl.lastIndexOf("/") + 1));
}
}
|
复杂度#
n:入参字符串长度
encode
:取决于gen
复杂度,考虑到冲突概率极低,每次gen
,仅需拼接codeLen
即八位的字符串,复杂度为O(codeLen)
。
decode
:需遍历短链、截取,O(n)
。
映射占用链长级别的空间,占用O(n)
。
hashids#
可以研究下 hashids 实现细节。