leetcode205 同构字符串 golang版

题目描述

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

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

示例 1:
//
//输入:s = “egg”, t = “add”
//输出:true
//示例 2:
//
//输入:s = “foo”, t = “bar”
//输出:false

解题思路

要判断两个字符串是否是同构的,我们需要确保:
从字符串 s 到 t 的字符映射在整个字符串中是一致的。
不能有两个不同的字符在 s 中映射到 t 中的同一个字符。
我们可以使用两个哈希表(或字典)来跟踪字符映射:
一个哈希表用来记录从 s 到 t 的映射关系。
另一个哈希表用来记录从 t 到 s 的映射关系。

解题步骤

1.首先检查 s 和 t 的长度是否相等。如果不相等,则它们不可能是同构的,直接返回 false。
2.同时遍历字符串 s 和 t 中的字符。
3.对于每对字符 s[i] 和 t[i]:
4.如果 s[i] 已经在哈希表中映射过,检查其映射的字符是否为 t[i]。如果不是,返回 false。
5.如果 t[i] 已经在哈希表中映射过,检查其映射的字符是否为 s[i]。如果不是,返回 false。
6.如果两个字符都没有被映射过,则在两个哈希表中分别添加对应的映射关系。
7.如果遍历完整个字符串没有发现不一致的映射关系,则返回 true。

代码实现

func isIsomorphic(s string, t string) bool {
	if len(s) != len(t) {
		return false
	}

	// 用两个map来记录
	// 这里为什么用byte
	mapS := make(map[byte]byte)
	mapT := make(map[byte]byte)
	// 同时遍历
	for i := 0; i < len(s); i++ {
		charS := s[i]
		charT := t[i]
		// 检查s到t的映射是否存在且一致
		if mappendChar, ok := mapS[charS]; ok {
			if mappendChar != charT {
				return false
			}
		} else {
			mapS[charS] = charT
		}

		// 检查t到s的映射是否存在且一致
		if mappendChar, ok := mapT[charT]; ok {
			if mappendChar != charS {
				return false
			}
		} else {
			mapT[charT] = charS
		}
	}
	return true
}

代码测试

func main() {
	// 测试用例
	testCases := []struct {
		s        string
		t        string
		expected bool
	}{
		{"egg", "add", true},
		{"foo", "bar", false},
		{"paper", "title", true},
		{"ab", "aa", false},
		{"a", "a", true},
	}

	for _, tc := range testCases {
		result := isIsomorphic(tc.s, tc.t)
		fmt.Printf("isIsomorphic(%q, %q) = %v, expected = %v\n", tc.s, tc.t, result, tc.expected)
	}
}

测试结果

在这里插入图片描述

关于题目疑问

Q1 为什么map用byte类型来构建

  1. 字符串的底层表示:

在 Go 语言中,字符串实际上是一个字节序列([]byte),每个字符都是一个 byte(即 8 位无符号整数,范围是 0-255)。
符串索引访问: 当我们访问字符串的某个字符时,例如 s[i],返回的是一个 byte 类型的数据。这是因为字符串在 Go 中是以 UTF-8 编码存储的,每个字符(如果是 ASCII 字符)可以直接用 byte 表示。

  1. 字节类型的高效性:

使用 byte 作为键值有以下几个优势:
性能优化: byte 类型只占用 1 字节(8 位),而 int 或 rune 类型可能会占用更多的内存(例如 int 通常是 4 字节或 8 字节)。在处理大量字符映射时,使用 byte 可以减少内存占用,并提高查找和插入操作的效率。
避免多余的类型转换: 如果直接使用 s[i] 作为键,它已经是 byte 类型,避免了将其转换为 rune 或 int 的开销。

  1. 与 UTF-8 兼容:

Go 的字符串是以 UTF-8 编码的字节序列存储的,UTF-8 的每个字符占用 1 到 4 个字节。对 ASCII 字符(范围在 0 到 127)的操作,使用 byte 类型已经足够,因为 ASCII 字符只占用 1 个字节。对于更复杂的 Unicode 字符,这个算法假设输入的字符串由简单的单字节字符组成(如英文字母),因此使用 byte 是合适且高效的。

总结

在这个算法中使用 byte 作为 map 的键类型主要是因为:
字符串的每个字符在 Go 中可以直接被表示为 byte 类型(如果字符是 ASCII 字符)。
byte 类型的存储和处理更加高效,节省了内存和计算资源。
避免了不必要的类型转换,提高了代码的性能和可读性。
因此,使用 byte 作为键类型是一个符合 Go 语言设计理念的选择,特别是在处理字符串时。

Q2 为什么这样能判断同构?

同构字符串定义

两个字符串是同构的,如果 s 中的字符可以通过某种一对一的映射关系被替换为 t 中的字符,且所有字符的出现顺序必须保持一致。

例子分析
举例来说,abb 和 egg 是同构的,因为可以通过如下映射来匹配:
a -> e
b -> g
这意味着字符串 s 中的每个字符都可以找到一个对应的字符 t,并且这个映射在整个字符串中是一致的。

为什么这样能判断同构?

为了判断两个字符串是否同构,我们需要满足以下条件:
一致性:字符串 s 中的每个字符只能映射到 t 中的一个唯一字符。
唯一性:t 中的每个字符只能对应 s 中的一个唯一字符。
详细分析
以 abb 和 egg 为例:
遍历第一个字符:a 和 e。a 映射到 e,建立映射关系 a -> e 和 e -> a。
遍历第二个字符:b 和 g。b 映射到 g,建立映射关系 b -> g 和 g -> b。
遍历第三个字符:b 和 g。已经有映射 b -> g,检查是否一致,结果一致,因此通过。
因为在整个过程中,s 中的字符映射到 t 中的字符是一致的,同时没有冲突的映射关系,所以可以判断 abb 和 egg 是同构的。
如果我们换一个例子,比如 foo 和 bar:
f 映射到 b,建立映射关系 f -> b 和 b -> f。
o 映射到 a,建立映射关系 o -> a 和 a -> o。
遍历第三个字符时,o 已经有映射为 a,但是第三个字符 r 并不是 a,所以 foo 和 bar 不是同构的。
关键点
同构的关键在于字符的映射:
字符映射的唯一性(一个字符只能映射到一个字符)。
字符映射的一致性(同一个字符在不同位置的映射必须一致)。
如果两个字符串在这两个条件下都满足一致的映射关系,那么这两个字符串就是同构的。
通过这种方式,我们可以很容易地判断两个字符串是否同构。

测试不通过修改

isIsomorphic(“badc”, “baba”) 结果为true

func isIsomorphic(s string, t string) bool {
	if len(s) != len(t) {
		return false
	}

	// 两个哈希表分别记录从 s 到 t 和从 t 到 s 的映射
	mapS := make(map[byte]byte)
	mapT := make(map[byte]byte)

	for i := 0; i < len(s); i++ {
		charS := s[i]
		charT := t[i]

		// 检查是否存在从 s 到 t 的映射
		if mappedChar, ok := mapS[charS]; ok {
			// 如果存在映射,但不匹配,则返回 false
			if mappedChar != charT {
				return false
			}
		} else {
			// 在设置映射之前,确保 t[i] 没有被映射到其他字符
			if mappedChar, ok := mapT[charT]; ok && mappedChar != charS {
				return false
			}
			// 建立 s 到 t 的映射
			mapS[charS] = charT
		}

		// 检查是否存在从 t 到 s 的映射
		if mappedChar, ok := mapT[charT]; ok {
			// 如果存在映射,但不匹配,则返回 false
			if mappedChar != charS {
				return false
			}
		} else {
			// 建立 t 到 s 的映射
			mapT[charT] = charS
		}
	}

	return true
}

  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 使用reverse函数反转leetcode字符串的方法是:将字符串作为参数传入reverse函数,然后将反转后的字符串返回给调用者。例如:let reversedString = reverse("leetcode")。 ### 回答2: 要使用reverse函数反转leetcode这个字符串,可以使用以下步骤: 1. 将字符串"leetcode"转换为字符数组。可以使用字符串的toCharArray()方法,该方法会返回一个字符数组,其中每个元素是字符串的一个字符。 2. 定义两个指针,left和right,分别指向字符数组的首尾。 3. 循环交换left和right指向的字符,然后将left指针向右移动一位,将right指针向左移动一位,直到left指针大于等于right指针。 4. 将反转后的字符数组转换回字符串。可以使用字符数组的valueOf()方法,该方法会将字符数组转换为一个新的字符串。 以下是用Java代码实现上述步骤的示例: ```java public String reverseString(String s) { // 将字符串转换为字符数组 char[] chars = s.toCharArray(); // 定义两个指针 int left = 0; int right = chars.length - 1; // 循环交换字符 while (left < right) { char temp = chars[left]; chars[left] = chars[right]; chars[right] = temp; left++; right--; } // 将字符数组转换为字符串 return String.valueOf(chars); } ``` 通过调用reverseString("leetcode")函数,将会返回字符串"edocteel",即将"leetcode"反转的结果。 ### 回答3: 要使用reverse函数来反转字符串"leetcode",只需将字符串转换为一个列表,然后使用reverse函数来翻转该列表,最后将列表转回字符串即可。 首先,将字符串"leetcode"转换为一个字符列表,可以使用list函数来实现,即list("leetcode"),得到列表['l', 'e', 'e', 't', 'c', 'o', 'd', 'e']。 接下来,使用reverse函数来反转该列表,即reverse(['l', 'e', 'e', 't', 'c', 'o', 'd', 'e'])。 最后,将翻转后的列表转换回字符串,可以使用join函数将列表中的字符连接起来,即''.join(['e', 'd', 'o', 'c', 't', 'e', 'e', 'l'])。 所以,将字符列表['l', 'e', 'e', 't', 'c', 'o', 'd', 'e']翻转并转换为字符串的过程为:''.join(reverse(list("leetcode"))),得到的结果为"edocteel",即字符串"leetcode"反转后的结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值