|
| 1 | +# 题目描述(简单难度) |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | +判断两个字符串是否是同构的。 |
| 6 | + |
| 7 | +# 解法一 |
| 8 | + |
| 9 | +题目描述中已经很详细了,两个字符串同构的含义就是字符串 `s` 可以唯一的映射到 `t` ,同时 `t` 也可以唯一的映射到 `s` 。 |
| 10 | + |
| 11 | +举个具体的例子。 |
| 12 | + |
| 13 | +```java |
| 14 | +egg 和 add 同构,就意味着下边的映射成立 |
| 15 | +e -> a |
| 16 | +g -> d |
| 17 | +也就是将 egg 的 e 换成 a, g 换成 d, 就变成了 add |
| 18 | + |
| 19 | +同时倒过来也成立 |
| 20 | +a -> e |
| 21 | +d -> g |
| 22 | +也就是将 add 的 a 换成 e, d 换成 g, 就变成了 egg |
| 23 | + |
| 24 | +foo 和 bar 不同构,原因就是映射不唯一 |
| 25 | +o -> a |
| 26 | +o -> r |
| 27 | +其中 o 映射到了两个字母 |
| 28 | +``` |
| 29 | + |
| 30 | +我们可以利用一个 `map` 来处理映射。对于 `s` 到 `t` 的映射,我们同时遍历 `s` 和 `t` ,假设当前遇到的字母分别是 `c1` 和 `c2` 。 |
| 31 | + |
| 32 | +如果 `map[c1]` 不存在,那么就将 `c1` 映射到 `c2` ,即 `map[c1] = c2`。 |
| 33 | + |
| 34 | +如果 `map[c1]` 存在,那么就判断 `map[c1]` 是否等于 `c2`,也就是验证之前的映射和当前的字母是否相同。 |
| 35 | + |
| 36 | +```java |
| 37 | +private boolean isIsomorphicHelper(String s, String t) { |
| 38 | + int n = s.length(); |
| 39 | + HashMap<Character, Character> map = new HashMap<>(); |
| 40 | + for (int i = 0; i < n; i++) { |
| 41 | + char c1 = s.charAt(i); |
| 42 | + char c2 = s.charAt(i); |
| 43 | + if (map.containsKey(c1)) { |
| 44 | + if (map.get(c1) != c2) { |
| 45 | + return false; |
| 46 | + } |
| 47 | + } else { |
| 48 | + map.put(c1, c2); |
| 49 | + } |
| 50 | + } |
| 51 | + return true; |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +对于这道题,我们只需要验证 `s - > t` 和 `t -> s` 两个方向即可。如果验证一个方向,是不可以的。 |
| 56 | + |
| 57 | +举个例子,`s = ab, t = cc`,如果单看 `s -> t` ,那么 `a -> c, b -> c` 是没有问题的。 |
| 58 | + |
| 59 | +必须再验证 `t -> s`,此时,`c -> a, c -> b`,一个字母对应了多个字母,所以不是同构的。 |
| 60 | + |
| 61 | +代码的话,只需要调用两次上边的代码即可。 |
| 62 | + |
| 63 | +```java |
| 64 | +public boolean isIsomorphic(String s, String t) { |
| 65 | + return isIsomorphicHelper(s, t) && isIsomorphicHelper(t, s); |
| 66 | + |
| 67 | +} |
| 68 | + |
| 69 | +private boolean isIsomorphicHelper(String s, String t) { |
| 70 | + int n = s.length(); |
| 71 | + HashMap<Character, Character> map = new HashMap<>(); |
| 72 | + for (int i = 0; i < n; i++) { |
| 73 | + char c1 = s.charAt(i); |
| 74 | + char c2 = t.charAt(i); |
| 75 | + if (map.containsKey(c1)) { |
| 76 | + if (map.get(c1) != c2) { |
| 77 | + return false; |
| 78 | + } |
| 79 | + } else { |
| 80 | + map.put(c1, c2); |
| 81 | + } |
| 82 | + } |
| 83 | + return true; |
| 84 | +} |
| 85 | +``` |
| 86 | + |
| 87 | +# 解法二 |
| 88 | + |
| 89 | +另一种思想,参考 [这里](https://leetcode.com/problems/isomorphic-strings/discuss/57796/My-6-lines-solution) 。 |
| 90 | + |
| 91 | +解法一中,我们判断 `s` 和 `t` 是否一一对应,通过对两个方向分别考虑来解决的。 |
| 92 | + |
| 93 | +这里的话,我们找一个第三方来解决,即,按照字母出现的顺序,把两个字符串都映射到另一个集合中。 |
| 94 | + |
| 95 | +举个现实生活中的例子,一个人说中文,一个人说法语,怎么判断他们说的是一个意思呢?把中文翻译成英语,把法语也翻译成英语,然后看最后的英语是否相同即可。 |
| 96 | + |
| 97 | +```java |
| 98 | +将第一个出现的字母映射成 1,第二个出现的字母映射成 2 |
| 99 | + |
| 100 | +对于 egg |
| 101 | +e -> 1 |
| 102 | +g -> 2 |
| 103 | +也就是将 egg 的 e 换成 1, g 换成 2, 就变成了 122 |
| 104 | + |
| 105 | +对于 add |
| 106 | +a -> 1 |
| 107 | +d -> 2 |
| 108 | +也就是将 add 的 a 换成 1, d 换成 2, 就变成了 122 |
| 109 | + |
| 110 | +egg -> 122, add -> 122 |
| 111 | +都变成了 122,所以两个字符串异构。 |
| 112 | +``` |
| 113 | + |
| 114 | +代码的话,只需要将两个字符串分别翻译成第三种类型即可。我们可以定义一个变量 `count = 1`,映射给出现的字母,然后进行自增。 |
| 115 | + |
| 116 | +```java |
| 117 | +public boolean isIsomorphic(String s, String t) { |
| 118 | + return isIsomorphicHelper(s).equals(isIsomorphicHelper(t)); |
| 119 | +} |
| 120 | + |
| 121 | +private String isIsomorphicHelper(String s) { |
| 122 | + int[] map = new int[128]; |
| 123 | + int n = s.length(); |
| 124 | + StringBuilder sb = new StringBuilder(); |
| 125 | + int count = 1; |
| 126 | + for (int i = 0; i < n; i++) { |
| 127 | + char c = s.charAt(i); |
| 128 | + //当前字母第一次出现,赋值 |
| 129 | + if (map[c] == 0) { |
| 130 | + map[c] = count; |
| 131 | + count++; |
| 132 | + } |
| 133 | + sb.append(map[c]); |
| 134 | + } |
| 135 | + return sb.toString(); |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +为了方便,我们也可以将当前字母直接映射为当前字母的下标加 `1`。因为 `0` 是我们的默认值,所以不能直接赋值为下标,而是「下标加 `1`」。 |
| 140 | + |
| 141 | +```java |
| 142 | +public boolean isIsomorphic(String s, String t) { |
| 143 | + return isIsomorphicHelper(s).equals(isIsomorphicHelper(t)); |
| 144 | +} |
| 145 | + |
| 146 | +private String isIsomorphicHelper(String s) { |
| 147 | + int[] map = new int[128]; |
| 148 | + int n = s.length(); |
| 149 | + StringBuilder sb = new StringBuilder(); |
| 150 | + for (int i = 0; i < n; i++) { |
| 151 | + char c = s.charAt(i); |
| 152 | + //当前字母第一次出现,赋值 |
| 153 | + if (map[c] == 0) { |
| 154 | + map[c] = i + 1; |
| 155 | + } |
| 156 | + sb.append(map[c]); |
| 157 | + } |
| 158 | + return sb.toString(); |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +上边的 `isIsomorphicHelper` 中我们通过 `map` 记录了当前字母要映射到哪个数字,然后最终返回了整个转换后的字符串。 |
| 163 | + |
| 164 | +其实我们不需要将字符串完全转换,我们可以用两个 `map` 分别记录两个字符串每个字母的映射。将所有字母初始都映射到 `0`。记录过程中,如果发现了当前映射不一致,就可以立即返回 `false` 了。 |
| 165 | + |
| 166 | +举个例子。 |
| 167 | + |
| 168 | +```java |
| 169 | +对 abaddee 和 cdbccff |
| 170 | + |
| 171 | +a b a d d e e |
| 172 | +c d b c c f f |
| 173 | +^ |
| 174 | + |
| 175 | +当前 |
| 176 | +a -> 0 |
| 177 | +c -> 0 |
| 178 | + |
| 179 | +修改映射 |
| 180 | +a -> 1 |
| 181 | +c -> 1 |
| 182 | + |
| 183 | +a b a d d e e |
| 184 | +c d b c c f f |
| 185 | + ^ |
| 186 | + |
| 187 | +当前 |
| 188 | +b -> 0 |
| 189 | +d -> 0 |
| 190 | + |
| 191 | +修改映射 |
| 192 | +b -> 2 |
| 193 | +d -> 2 |
| 194 | + |
| 195 | + |
| 196 | +a b a d d e e |
| 197 | +c d b c c f f |
| 198 | + ^ |
| 199 | +当前 |
| 200 | +a -> 1 (之前被修改过) |
| 201 | +b -> 0 |
| 202 | + |
| 203 | +出现不一致,所以两个字符串不异构 |
| 204 | +``` |
| 205 | + |
| 206 | +代码的话,用两个 `map` 记录映射即可。 |
| 207 | + |
| 208 | +```java |
| 209 | +public boolean isIsomorphic(String s, String t) { |
| 210 | + int n = s.length(); |
| 211 | + int[] mapS = new int[128]; |
| 212 | + int[] mapT = new int[128]; |
| 213 | + for (int i = 0; i < n; i++) { |
| 214 | + char c1 = s.charAt(i); |
| 215 | + char c2 = t.charAt(i); |
| 216 | + //当前的映射值是否相同 |
| 217 | + if (mapS[c1] != mapT[c2]) { |
| 218 | + return false; |
| 219 | + } else { |
| 220 | + //是否已经修改过,修改过就不需要再处理 |
| 221 | + if (mapS[c1] == 0) { |
| 222 | + mapS[c1] = i + 1; |
| 223 | + mapT[c2] = i + 1; |
| 224 | + } |
| 225 | + } |
| 226 | + } |
| 227 | + return true; |
| 228 | +} |
| 229 | +``` |
| 230 | + |
| 231 | +# 总 |
| 232 | + |
| 233 | +解法一就是我们比较常规的思路,解法二通过一个第三方的集合,将代码大大简化了,太巧妙了! |
| 234 | + |
| 235 | +题目其实有点像映射的知识,两个字符串为两个集合,然后判断当前映射是否为单射。 |
0 commit comments