当前位置: 首页 > news >正文

【华为机试】3. 无重复字符的最长子串

文章目录

  • 3. 无重复字符的最长子串
    • 📋 题目描述
    • 🎯 示例
      • 示例1
      • 示例2
      • 示例3
    • 🔍 解题思路
      • 核心思想:滑动窗口
      • 算法流程
      • 详细步骤
    • 🚀 算法实现
      • 方法1:暴力法(时间复杂度O(n³))
      • 方法2:滑动窗口法(时间复杂度O(n))
      • 方法3:优化的滑动窗口(使用数组)
    • 📊 算法分析
      • 时间复杂度对比
      • 滑动窗口过程可视化
    • 🎯 关键技巧
      • 1. 滑动窗口的本质
      • 2. 哈希表的作用
      • 3. 边界条件处理
    • 🔧 实际应用
      • 1. 字符串处理
      • 2. 网络协议
      • 3. 算法竞赛
    • 📈 性能优化
      • 1. 空间优化
      • 2. 时间优化
      • 3. 内存优化
    • 🧪 测试用例
      • 基础测试
      • 边界测试
    • 💡 扩展思考
      • 1. 变种问题
      • 2. 优化方向
      • 3. 实际应用扩展
    • 🎯 总结
    • 完整题解代码

3. 无重复字符的最长子串

📋 题目描述

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

🎯 示例

示例1

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例2

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例3

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

🔍 解题思路

核心思想:滑动窗口

这是一个经典的滑动窗口问题。我们需要维护一个窗口,使得窗口内的字符都是唯一的,并且尽可能地扩大这个窗口。

算法流程

开始
初始化左右指针和哈希表
右指针向右移动
当前字符是否在窗口中?
移动左指针到重复字符的下一位
将字符加入哈希表
更新最大长度
右指针到达末尾?
返回最大长度

详细步骤

  1. 初始化:设置左指针 left = 0,右指针 right = 0,哈希表记录字符位置
  2. 扩展窗口:右指针向右移动,将字符加入窗口
  3. 处理重复:如果遇到重复字符,移动左指针到重复字符的下一位
  4. 更新结果:每次移动后更新最大长度
  5. 继续直到结束:重复步骤2-4直到右指针到达字符串末尾

🚀 算法实现

方法1:暴力法(时间复杂度O(n³))

func lengthOfLongestSubstringBruteForce(s string) int {// 枚举所有子串,检查是否包含重复字符// 时间复杂度:O(n³)// 空间复杂度:O(min(m,n))
}

方法2:滑动窗口法(时间复杂度O(n))

func lengthOfLongestSubstring(s string) int {charMap := make(map[byte]int)left := 0maxLength := 0for right := 0; right < len(s); right++ {if pos, exists := charMap[s[right]]; exists && pos >= left {left = pos + 1}charMap[s[right]] = rightmaxLength = max(maxLength, right-left+1)}return maxLength
}

方法3:优化的滑动窗口(使用数组)

func lengthOfLongestSubstringOptimized(s string) int {// 使用数组代替哈希表,提高性能// 适用于ASCII字符集
}

📊 算法分析

时间复杂度对比

方法时间复杂度空间复杂度特点
暴力法O(n³)O(min(m,n))简单直观,效率低
滑动窗口O(n)O(min(m,n))高效,推荐使用
优化滑动窗口O(n)O(m)最优性能,固定空间

滑动窗口过程可视化

以字符串 “abcabcbb” 为例:

步骤  左指针  右指针  当前窗口    长度  最大长度
1     0      0      a          1     1
2     0      1      ab         2     2
3     0      2      abc        3     3
4     1      3      bca        3     3
5     2      4      cab        3     3
6     3      5      abc        3     3
7     4      6      bcb        3     3
8     6      7      b          1     3

🎯 关键技巧

1. 滑动窗口的本质

  • 扩展:右指针不断向右移动扩大窗口
  • 收缩:当遇到重复字符时,左指针跳跃到合适位置

2. 哈希表的作用

  • 快速检查字符是否在当前窗口中
  • 记录字符最后出现的位置
  • 支持O(1)时间的查找和更新

3. 边界条件处理

  • 空字符串:返回0
  • 单字符:返回1
  • 全相同字符:返回1
  • 全不同字符:返回字符串长度

🔧 实际应用

1. 字符串处理

  • 文本编辑器中的重复检测
  • 密码强度验证
  • 数据去重

2. 网络协议

  • TCP滑动窗口协议
  • HTTP/2流控制
  • 缓存策略

3. 算法竞赛

  • 子数组/子串问题的通用解法
  • 双指针技巧的典型应用
  • 哈希表优化的经典案例

📈 性能优化

1. 空间优化

// 使用数组代替哈希表(仅适用于ASCII)
lastIndex := make([]int, 128)

2. 时间优化

// 避免重复计算
maxLength = max(maxLength, right-left+1)

3. 内存优化

// 预分配合适大小的哈希表
charMap := make(map[byte]int, len(s))

🧪 测试用例

基础测试

testCases := []struct {input    stringexpected int
}{{"abcabcbb", 3},{"bbbbb", 1},{"pwwkew", 3},{"", 0},{"au", 2},{"dvdf", 3},
}

边界测试

extremeCases := []struct {input    stringexpected int
}{{"a", 1},                    // 单字符{"abcdefghijklmnopqrstuvwxyz", 26}, // 全不同{"aaaaaaaaaa", 1},           // 全相同{" ", 1},                    // 空格字符{"!@#$%^&*()", 10},         // 特殊字符
}

💡 扩展思考

1. 变种问题

  • 最多包含k个不同字符的最长子串
  • 最长回文子串
  • 最长公共子串

2. 优化方向

  • 并行处理长字符串
  • 支持Unicode字符集
  • 内存映射大文件处理

3. 实际应用扩展

  • 日志分析中的模式识别
  • 基因序列分析
  • 数据压缩算法

🎯 总结

无重复字符的最长子串问题是滑动窗口技巧的经典应用:

  1. 核心思想:维护一个动态窗口,保证窗口内字符唯一
  2. 关键技巧:使用哈希表快速检测重复字符
  3. 时间复杂度:从O(n³)优化到O(n)
  4. 空间复杂度:O(min(m,n)),m为字符集大小
  5. 实际应用:字符串处理、网络协议、算法竞赛

通过这道题,我们学会了滑动窗口的基本思想和实现技巧,为解决更复杂的字符串问题打下了坚实基础。


完整题解代码

package mainimport ("fmt""time"
)// 方法1:暴力法 - 时间复杂度O(n³)
func lengthOfLongestSubstringBruteForce(s string) int {n := len(s)if n == 0 {return 0}maxLength := 1// 枚举所有子串for i := 0; i < n; i++ {for j := i + 1; j <= n; j++ {if isUnique(s[i:j]) {if j-i > maxLength {maxLength = j - i}}}}return maxLength
}// 检查字符串是否包含重复字符
func isUnique(s string) bool {charSet := make(map[byte]bool)for i := 0; i < len(s); i++ {if charSet[s[i]] {return false}charSet[s[i]] = true}return true
}// 方法2:滑动窗口法 - 时间复杂度O(n)
func lengthOfLongestSubstring(s string) int {if len(s) == 0 {return 0}charMap := make(map[byte]int)left := 0maxLength := 0for right := 0; right < len(s); right++ {// 如果当前字符在窗口中已存在if pos, exists := charMap[s[right]]; exists && pos >= left {left = pos + 1}charMap[s[right]] = right// 更新最大长度newLength := right - left + 1if newLength > maxLength {maxLength = newLength}}return maxLength
}// 方法3:优化的滑动窗口法(使用数组代替哈希表)
func lengthOfLongestSubstringOptimized(s string) int {if len(s) == 0 {return 0}// 使用数组存储字符最后出现的位置lastIndex := make([]int, 128) // ASCII字符集for i := range lastIndex {lastIndex[i] = -1}left := 0maxLength := 0for right := 0; right < len(s); right++ {char := s[right]// 如果当前字符在窗口中已存在if lastIndex[char] >= left {left = lastIndex[char] + 1}lastIndex[char] = right// 更新最大长度if right-left+1 > maxLength {maxLength = right - left + 1}}return maxLength
}// 方法4:滑动窗口法(带详细过程展示)
func lengthOfLongestSubstringWithTrace(s string) int {if len(s) == 0 {return 0}fmt.Printf("输入字符串: %s\n", s)fmt.Println("滑动窗口过程:")fmt.Println("步骤\t左指针\t右指针\t当前窗口\t\t长度\t最大长度")charMap := make(map[byte]int)left := 0maxLength := 0step := 0for right := 0; right < len(s); right++ {step++// 如果当前字符在窗口中已存在if pos, exists := charMap[s[right]]; exists && pos >= left {left = pos + 1}charMap[s[right]] = right// 更新最大长度currentLength := right - left + 1if currentLength > maxLength {maxLength = currentLength}// 打印当前状态window := s[left : right+1]fmt.Printf("%d\t%d\t%d\t%s\t\t%d\t%d\n",step, left, right, window, currentLength, maxLength)}return maxLength
}// 性能测试函数
func performanceTest() {testCases := []string{"abcabcbb","bbbbb","pwwkew","","abcdefghijklmnopqrstuvwxyz","abcabcabcabcabc","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",}fmt.Println("\n=== 性能测试 ===")fmt.Println("测试用例\t\t\t暴力法耗时\t滑动窗口耗时\t优化版耗时")for _, testCase := range testCases {// 暴力法测试start := time.Now()result1 := lengthOfLongestSubstringBruteForce(testCase)time1 := time.Since(start)// 滑动窗口法测试start = time.Now()result2 := lengthOfLongestSubstring(testCase)time2 := time.Since(start)// 优化版测试start = time.Now()result3 := lengthOfLongestSubstringOptimized(testCase)time3 := time.Since(start)// 验证结果一致性if result1 != result2 || result2 != result3 {fmt.Printf("错误:结果不一致!%d, %d, %d\n", result1, result2, result3)}displayCase := testCaseif len(displayCase) > 15 {displayCase = displayCase[:15] + "..."}fmt.Printf("%-20s\t%v\t\t%v\t\t%v\n",displayCase, time1, time2, time3)}
}func main() {// 基本测试用例testCases := []string{"abcabcbb","bbbbb","pwwkew","","au","dvdf",}fmt.Println("=== 基本测试 ===")for _, testCase := range testCases {result := lengthOfLongestSubstring(testCase)fmt.Printf("输入: \"%s\" -> 输出: %d\n", testCase, result)}fmt.Println("\n=== 详细过程展示 ===")lengthOfLongestSubstringWithTrace("abcabcbb")// 性能测试performanceTest()fmt.Println("\n=== 算法复杂度分析 ===")fmt.Println("暴力法:")fmt.Println("  时间复杂度:O(n³)")fmt.Println("  空间复杂度:O(min(m,n)) - m为字符集大小")fmt.Println("\n滑动窗口法:")fmt.Println("  时间复杂度:O(n)")fmt.Println("  空间复杂度:O(min(m,n)) - m为字符集大小")fmt.Println("\n优化版滑动窗口:")fmt.Println("  时间复杂度:O(n)")fmt.Println("  空间复杂度:O(m) - 固定大小数组")
}

文章转载自:
http://bios.sxnf.com.cn
http://verge.sxnf.com.cn
http://alep.sxnf.com.cn
http://melilite.sxnf.com.cn
http://breadth.sxnf.com.cn
http://coursing.sxnf.com.cn
http://brinish.sxnf.com.cn
http://fissile.sxnf.com.cn
http://deknight.sxnf.com.cn
http://sew.sxnf.com.cn
http://tripolitania.sxnf.com.cn
http://necrotizing.sxnf.com.cn
http://paisan.sxnf.com.cn
http://phototypy.sxnf.com.cn
http://puritanical.sxnf.com.cn
http://illuminism.sxnf.com.cn
http://horae.sxnf.com.cn
http://agronomics.sxnf.com.cn
http://intentional.sxnf.com.cn
http://decisionmaker.sxnf.com.cn
http://dunite.sxnf.com.cn
http://olive.sxnf.com.cn
http://pourable.sxnf.com.cn
http://cipherkey.sxnf.com.cn
http://quintefoil.sxnf.com.cn
http://creamwove.sxnf.com.cn
http://conservatize.sxnf.com.cn
http://belvedere.sxnf.com.cn
http://threatening.sxnf.com.cn
http://pickup.sxnf.com.cn
http://www.dtcms.com/a/280738.html

相关文章:

  • 光米投影 多余之物的本思
  • 怎么样才能入门深度学习?
  • element plus使用插槽方式自定义el-form-item的label
  • NW917NW921美光固态闪存NW946NW952
  • 1.2 vue2(组合式API)的语法结构以及外部暴露
  • Vue框架之模板语法(插值表达式、指令系统、事件处理和表单绑定)全面解析
  • MATLAB 安装 ACADO 的完整步骤
  • 简单工厂设计模式
  • Web应用防火墙(WAF)技术
  • vue实现el-table-column中自定义label
  • 全局 WAF 规则:构筑 Web 安全的坚固防线
  • Git基础概念与常用命令详解
  • x86版的ubuntu上使用qemu运行arm版ubuntu
  • Java项目:基于SSM框架实现的网盘管理系统【ssm+B/S架构+源码+数据库+毕业论文】
  • EMS4100芯祥科技USB3.1高速模拟开关芯片规格介绍
  • iOS高级开发工程师面试——多线程
  • LVS三种模式实战
  • Unity沉浸式/360View/全景渲染
  • 新手向:Python数据处理Excel报表自动化生成与分析
  • Zookeeper入门安装与使用详解
  • Java行为型模式---迭代器模式
  • Linux、Ubuntu和CentOS的关系与区别
  • 设计模式笔记_结构型_适配器模式
  • 3.正则化——新闻分类
  • 生产问题排查-数据库连接池耗尽
  • 牛客:HJ23 删除字符串中出现次数最少的字符[华为机考][字符串]
  • Linux 环境下安装 Node.js v16.13.0 完整指南
  • MongoDB 数据库 启用访问控制
  • Volta现代化的 Node.js 版本管理工具
  • CSRF 攻击原理与实验测试(附可运行测试案例)