无重复字符的最长子串 Golang leecode_3

刚开始的思路,先不管效率,跑出来再说,然后再进行优化。然后就有了下面的暴力代码:

func lengthOfLongestSubstring(s string) int {
	// count 用来记录当前最长子串长度
    var count int
    // flag 用来对下面两个 if 语句分流
	var flag int = 0
	// for 对字符串进行遍历
	for i := 0; i < len(s); i++ {
		// mark 用来记录当前子串,初始为当前遍历遇到的第一个字符
		mark := string(s[i])
		// 这个 for 循环用来接着 mark 往后遍历,如果遇到的字符不在 mark 里,就往 mark 后面接
		for j := i + 1; j < len(s); j++ {
			// strings.Contain(A string, B string) 用来判断 A 中是否有 B,有返回 true, 没有返回 false
			if strings.Contains(mark, string(s[j])) == true {
				// 有的话就 break, 当前是一个完整的无重复子串,再往后接就重复了
				break
			}
			if strings.Contains(mark, string(s[j])) == false {
				// 没有说明还能接着往后接,就继续遍历
				mark += string(s[j])
			}
		}
		if flag == 0 {
			// flag == 0 表示这是该字符串遇到的第一个无重复子串,把长度记录下来
			count = len(mark)
			flag = 1
		} else if flag == 1 {
			// flag == 1 表示这以及不是第一个子串了,那么就计算当前无重复子串的长度
			// 和原先比较,更长的话就重新赋值给 count, 否则就不重新赋值
			if count < len(mark) {
				count = len(mark)
			}
		}
	}
	return count
}

跑是跑出来了,时间 300ms (仅超过 5% 用户),内存 6.44MB (仅超过 7% 用户),那我得优化以下。我寻思能不能用字符指针,让源代码减少一个 for 循环?emmm 然后我就开始写代码,但是我发现指针并不能减少一个 for 循环,因为始终需要一个 for 来遍历子串起始位置,另一个 for 用来移动指针,写都写了,就上代码吧:

package main

import (
	"fmt"
	"strings"
	"unsafe"
)

func lengthOfLongestSubstring(s string) int {
	// 先将字符串变成字符数组,采用用指针遍历
	bytes := []byte(s)
	// 定义字符指针
	var bytePtr *byte
	// flag 和 count 的作用上一版程序说过了,不赘述
	var flag int = 0
	var count int

	for i := 0; i < len(s); i++ {
		mark := string(s[i])
		for j := i + 1; j < len(s); j++ {
			// 指针指向 bytes[j] 位置的元素
			// golang 这种字符指针很麻烦,必须用 unsafe.Pointer() 进行转换
			bytePtr = (*byte)(unsafe.Pointer(&bytes[j]))
			if strings.Contains(mark, string(*bytePtr)) == false {
				// 如果该字符不在前面的子串中,则加入
				mark += string(*bytePtr)
				// 指针后移一位
				bytePtr = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(bytePtr)) + 1))
			}
			if strings.Contains(mark, string(*bytePtr)) == true {
				break
			}
		}
		if flag == 0 {
			count = len(mark)
			flag = 1
		} else if flag == 1 {
			if count < len(mark) {
				count = len(mark)
			}
		}
	}
	return count
}

func main() {
	var s = "pwwkew"
	fmt.Println(lengthOfLongestSubstring(s))
}

笑死了,时间 216ms (仅超过 5.84% 用户),内存 6.46MB (仅超过 6.98% 用户),几乎没有优化。我想着看看大佬都是怎么写的吧。发现大佬用的是滑动窗口,确实酷,来个大佬讲解视频的链接 点这里跳转,然后下面是代码,看不懂的朋友可以进行单步调试,我是边调边画图理解的。该程序运行时间 0ms,占用内存 2.26MB ,比我原方案效率高太多了,妙不可言。

package main

import (
	"fmt"
)

func lengthOfLongestSubstring(s string) (ans int) {
	window := [128]bool{} // 也可以用 map,这里为了效率用的数组
	left := 0
	for right, c := range s {
		for window[c] { // 加入 c 后,窗口内会有重复元素
			window[s[left]] = false
			left++
		}
		window[c] = true
		ans = max(ans, right-left+1) // 更新窗口长度最大值
	}
	return
}

func max(a, b int) int {
	if b > a {
		return b
	}
	return a
}

func main() {
	var s = "pwwkew"
	fmt.Println(lengthOfLongestSubstring(s))
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值