0205-同构字符串

Raphael Liu Lv10

给定两个字符串 st ,判断它们是否是同构的。

如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。

每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

示例 1:

**输入:** s = "egg", t = "add"
**输出:** true

示例 2:

**输入:** s = "foo", t = "bar"
**输出:** false

示例 3:

**输入:** s = "paper", t = "title"
**输出:** true

提示:

  • 1 <= s.length <= 5 * 104
  • t.length == s.length
  • st 由任意有效的 ASCII 字符组成

方法一:哈希表

此题是「290. 单词规律 」的简化版,需要我们判断 $s$ 和 $t$ 每个位置上的字符是否都一一对应,即 $s$ 的任意一个字符被 $t$ 中唯一的字符对应,同时 $t$ 的任意一个字符被 $s$ 中唯一的字符对应。这也被称为「双射」的关系。

以示例 $2$ 为例,$t$ 中的字符 $a$ 和 $r$ 虽然有唯一的映射 $o$,但对于 $s$ 中的字符 $o$ 来说其存在两个映射 ${a,r}$,故不满足条件。

因此,我们维护两张哈希表,第一张哈希表 $\textit{s2t}$ 以 $s$ 中字符为键,映射至 $t$ 的字符为值,第二张哈希表 $\textit{t2s}$ 以 $t$ 中字符为键,映射至 $s$ 的字符为值。从左至右遍历两个字符串的字符,不断更新两张哈希表,如果出现冲突(即当前下标 $\textit{index}$ 对应的字符 $s[\textit{index}]$ 已经存在映射且不为 $t[\textit{index}]$ 或当前下标 $\textit{index}$ 对应的字符 $t[\textit{index}]$ 已经存在映射且不为 $s[\textit{index}]$)时说明两个字符串无法构成同构,返回 $\rm false$。

如果遍历结束没有出现冲突,则表明两个字符串是同构的,返回 $\rm true$ 即可。

[sol1-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
bool isIsomorphic(string s, string t) {
unordered_map<char, char> s2t;
unordered_map<char, char> t2s;
int len = s.length();
for (int i = 0; i < len; ++i) {
char x = s[i], y = t[i];
if ((s2t.count(x) && s2t[x] != y) || (t2s.count(y) && t2s[y] != x)) {
return false;
}
s2t[x] = y;
t2s[y] = x;
}
return true;
}
};
[sol1-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public boolean isIsomorphic(String s, String t) {
Map<Character, Character> s2t = new HashMap<Character, Character>();
Map<Character, Character> t2s = new HashMap<Character, Character>();
int len = s.length();
for (int i = 0; i < len; ++i) {
char x = s.charAt(i), y = t.charAt(i);
if ((s2t.containsKey(x) && s2t.get(x) != y) || (t2s.containsKey(y) && t2s.get(y) != x)) {
return false;
}
s2t.put(x, y);
t2s.put(y, x);
}
return true;
}
}
[sol1-JavaScript]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var isIsomorphic = function(s, t) {
const s2t = {};
const t2s = {};
const len = s.length;
for (let i = 0; i < len; ++i) {
const x = s[i], y = t[i];
if ((s2t[x] && s2t[x] !== y) || (t2s[y] && t2s[y] !== x)) {
return false;
}
s2t[x] = y;
t2s[y] = x;
}
return true;
};
[sol1-Golang]
1
2
3
4
5
6
7
8
9
10
11
12
13
func isIsomorphic(s, t string) bool {
s2t := map[byte]byte{}
t2s := map[byte]byte{}
for i := range s {
x, y := s[i], t[i]
if s2t[x] > 0 && s2t[x] != y || t2s[y] > 0 && t2s[y] != x {
return false
}
s2t[x] = y
t2s[y] = x
}
return true
}
[sol1-C]
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
struct HashTable {
char key;
char val;
UT_hash_handle hh;
};

bool isIsomorphic(char* s, char* t) {
struct HashTable* s2t = NULL;
struct HashTable* t2s = NULL;
int len = strlen(s);
for (int i = 0; i < len; ++i) {
char x = s[i], y = t[i];
struct HashTable *tmp1, *tmp2;
HASH_FIND(hh, s2t, &x, sizeof(char), tmp1);
HASH_FIND(hh, t2s, &y, sizeof(char), tmp2);
if (tmp1 != NULL) {
if (tmp1->val != y) {
return false;
}
} else {
tmp1 = malloc(sizeof(struct HashTable));
tmp1->key = x;
tmp1->val = y;
HASH_ADD(hh, s2t, key, sizeof(char), tmp1);
}
if (tmp2 != NULL) {
if (tmp2->val != x) {
return false;
}
} else {
tmp2 = malloc(sizeof(struct HashTable));
tmp2->key = y;
tmp2->val = x;
HASH_ADD(hh, t2s, key, sizeof(char), tmp2);
}
}
return true;
}

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 为字符串的长度。我们只需同时遍历一遍字符串 $s$ 和 $t$ 即可。
  • 空间复杂度:$O(|\Sigma|)$,其中 $\Sigma$ 是字符串的字符集。哈希表存储字符的空间取决于字符串的字符集大小,最坏情况下每个字符均不相同,需要 $O(|\Sigma|)$ 的空间。
 Comments
On this page
0205-同构字符串