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

给公司做网站和公众号需要多少钱如何注册网站名称

给公司做网站和公众号需要多少钱,如何注册网站名称,校园论坛网站怎么做,简洁网站首页html代码文章目录38. 外观数列题目描述示例 1:示例 2:提示:解题思路算法分析核心思想算法对比算法流程图递归解法流程迭代解法流程优化迭代流程复杂度分析时间复杂度空间复杂度关键优化技巧1. 字符串构建优化2. 双指针技术优化3. 内存预分配优化4. 递…

文章目录

  • 38. 外观数列
    • 题目描述
    • 示例 1:
    • 示例 2:
    • 提示:
    • 解题思路
      • 算法分析
        • 核心思想
        • 算法对比
      • 算法流程图
      • 递归解法流程
      • 迭代解法流程
      • 优化迭代流程
      • 复杂度分析
        • 时间复杂度
        • 空间复杂度
      • 关键优化技巧
        • 1. 字符串构建优化
        • 2. 双指针技术优化
        • 3. 内存预分配优化
        • 4. 递归优化(尾递归)
      • 边界情况处理
        • 1. 输入验证
        • 2. 字符串处理
        • 3. 特殊情况
      • 算法优化策略
        • 1. 空间优化
        • 2. 时间优化
        • 3. 实现优化
      • 应用场景
      • 测试用例设计
        • 基础测试
        • 边界测试
        • 性能测试
      • 实战技巧总结
    • 代码实现
      • 方法一:递归解法
      • 方法二:迭代解法
      • 方法三:优化迭代
      • 方法四:双缓冲技术
    • 测试结果
      • 性能对比分析
    • 核心收获
    • 应用拓展
    • 完整题解代码

38. 外观数列

题目描述

「外观数列」是一个数位字符串序列,由递归公式定义:

countAndSay(1) = “1”
countAndSay(n) 是 countAndSay(n-1) 的行程长度编码。

行程长度编码(RLE)是一种字符串压缩方法,其工作原理是通过将连续相同字符(重复两次或更多次)替换为字符重复次数(运行长度)和字符的串联。例如,要压缩字符串 “3322251” ,我们将 “33” 用 “23” 替换,将 “222” 用 “32” 替换,将 “5” 用 “15” 替换并将 “1” 用 “11” 替换。因此压缩后字符串变为 “23321511”。

给定一个整数 n ,返回 外观数列 的第 n 个元素。

示例 1:

输入:n = 4

输出:“1211”

解释:

countAndSay(1) = “1”

countAndSay(2) = “1” 的行程长度编码 = “11”

countAndSay(3) = “11” 的行程长度编码 = “21”

countAndSay(4) = “21” 的行程长度编码 = “1211”

示例 2:

输入:n = 1

输出:“1”

解释:

这是基本情况。

提示:

  • 1 <= n <= 30

解题思路

算法分析

这是一道经典的字符串处理与递归问题,需要生成外观数列的第n项。核心思想是递归生成+行程长度编码:基于前一项生成下一项,使用RLE压缩算法。

核心思想
  1. 递归定义:第n项基于第n-1项生成
  2. 行程长度编码:统计连续相同字符的个数和字符本身
  3. 字符串构建:将统计结果转换为字符串格式
  4. 迭代优化:使用循环代替递归,提高空间效率
  5. 内存优化:避免不必要的字符串复制和内存分配
算法对比
算法时间复杂度空间复杂度特点
递归解法O(n×m)O(n×m)最直观的解法,递归生成每一项
迭代解法O(n×m)O(m)空间优化,使用循环代替递归
优化迭代O(n×m)O(m)字符串构建优化,减少内存分配
双缓冲O(n×m)O(m)使用双缓冲技术,避免频繁复制

注:n为项数,m为字符串平均长度

算法流程图

graph TDA[开始: 输入n] --> B{n == 1?}B -->|是| C[返回 "1"]B -->|否| D[初始化 result = "1"]D --> E[循环 i = 2 to n]E --> F[对result进行行程长度编码]F --> G[统计连续相同字符]G --> H[构建新的字符串]H --> I[更新result]I --> J{i < n?}J -->|是| EJ -->|否| K[返回result]C --> L[结束]K --> L

递归解法流程

graph TDA[递归解法开始] --> B[输入参数n]B --> C{n == 1?}C -->|是| D[返回基础情况 "1"]C -->|否| E[递归调用 countAndSay(n-1)]E --> F[获取前一项字符串]F --> G[对前一项进行RLE编码]G --> H[遍历字符串]H --> I[统计连续字符个数]I --> J[构建编码结果]J --> K[返回编码后的字符串]D --> L[结束]K --> L

迭代解法流程

graph TDA[迭代解法开始] --> B[初始化 result = "1"]B --> C[循环 i = 2 to n]C --> D[创建临时字符串 temp]D --> E[遍历result字符串]E --> F[记录当前字符和计数]F --> G[统计连续相同字符]G --> H[将计数和字符添加到temp]H --> I[继续下一个不同字符]I --> J{还有字符?}J -->|是| FJ -->|否| K[更新 result = temp]K --> L{i < n?}L -->|是| CL -->|否| M[返回result]M --> N[结束]

优化迭代流程

graph TDA[优化迭代开始] --> B[初始化 result = "1"]B --> C[预分配字符串缓冲区]C --> D[循环 i = 2 to n]D --> E[清空缓冲区]E --> F[遍历result字符串]F --> G[使用双指针技术]G --> H[左指针: 记录起始位置]H --> I[右指针: 扩展相同字符]I --> J[计算连续字符长度]J --> K[直接写入缓冲区]K --> L[更新左指针位置]L --> M{还有字符?}M -->|是| FM -->|否| N[构建最终字符串]N --> O{i < n?}O -->|是| DO -->|否| P[返回result]P --> Q[结束]

复杂度分析

时间复杂度
  • 递归解法:O(n×m),n次递归调用,每次处理长度为m的字符串
  • 迭代解法:O(n×m),n次循环,每次处理长度为m的字符串
  • 优化迭代:O(n×m),但常数因子更小,实际运行更快
  • 双缓冲:O(n×m),减少字符串复制开销
空间复杂度
  • 递归解法:O(n×m),递归栈深度为n,每层存储长度为m的字符串
  • 迭代解法:O(m),只需要存储当前字符串和临时字符串
  • 优化迭代:O(m),使用缓冲区优化内存使用
  • 双缓冲:O(m),双缓冲技术进一步优化空间

关键优化技巧

1. 字符串构建优化
// 使用strings.Builder提高字符串拼接效率
func countAndSayOptimized(n int) string {if n == 1 {return "1"}result := "1"for i := 2; i <= n; i++ {var builder strings.Builderj := 0for j < len(result) {count := 1// 统计连续相同字符的个数for j+count < len(result) && result[j] == result[j+count] {count++}// 添加计数和字符builder.WriteString(strconv.Itoa(count))builder.WriteByte(result[j])j += count}result = builder.String()}return result
}
2. 双指针技术优化
// 使用双指针技术避免重复遍历
func countAndSayDoublePointer(n int) string {if n == 1 {return "1"}result := "1"for i := 2; i <= n; i++ {var builder strings.Builderleft := 0for left < len(result) {right := left// 扩展右指针直到遇到不同字符for right < len(result) && result[right] == result[left] {right++}// 添加计数和字符builder.WriteString(strconv.Itoa(right - left))builder.WriteByte(result[left])left = right}result = builder.String()}return result
}
3. 内存预分配优化
// 预分配缓冲区大小,减少内存重分配
func countAndSayPreAlloc(n int) string {if n == 1 {return "1"}result := "1"for i := 2; i <= n; i++ {// 预估新字符串长度(通常比原字符串长)estimatedLen := len(result) * 2builder := strings.Builder{}builder.Grow(estimatedLen)j := 0for j < len(result) {count := 1for j+count < len(result) && result[j] == result[j+count] {count++}builder.WriteString(strconv.Itoa(count))builder.WriteByte(result[j])j += count}result = builder.String()}return result
}
4. 递归优化(尾递归)
// 使用尾递归优化空间使用
func countAndSayTailRecursive(n int) string {return countAndSayHelper(n, "1")
}func countAndSayHelper(n int, current string) string {if n == 1 {return current}next := encodeRLE(current)return countAndSayHelper(n-1, next)
}func encodeRLE(s string) string {var builder strings.Builderi := 0for i < len(s) {count := 1for i+count < len(s) && s[i] == s[i+count] {count++}builder.WriteString(strconv.Itoa(count))builder.WriteByte(s[i])i += count}return builder.String()
}

边界情况处理

1. 输入验证
  • 确保n在有效范围内(1≤n≤30)
  • 处理n=1的特殊情况
  • 验证输入参数的有效性
2. 字符串处理
  • 处理空字符串的情况
  • 处理单字符字符串
  • 处理所有字符相同的情况
3. 特殊情况
  • n=1时直接返回"1"
  • 字符串长度为1时的处理
  • 连续字符长度超过9的情况

算法优化策略

1. 空间优化
  • 使用迭代代替递归减少栈空间
  • 使用strings.Builder减少字符串拼接开销
  • 预分配缓冲区大小避免频繁重分配
2. 时间优化
  • 双指针技术避免重复遍历
  • 减少不必要的字符串复制
  • 优化字符计数算法
3. 实现优化
  • 内联函数减少调用开销
  • 使用位运算优化数字转换
  • 缓存计算结果避免重复计算

应用场景

  1. 数据压缩:行程长度编码的实际应用
  2. 字符串处理:学习字符串操作和模式匹配
  3. 递归算法:理解递归和迭代的转换
  4. 算法竞赛:字符串处理的基础题目
  5. 数学序列:研究数学序列的生成规律

测试用例设计

基础测试
  • n=1:基础情况
  • n=2:简单情况
  • n=4:中等复杂度
  • n=10:较大输入
边界测试
  • n=1:最小输入
  • n=30:最大输入
  • 字符串长度变化:测试不同长度
性能测试
  • 大规模n值测试
  • 字符串长度增长测试
  • 内存使用测试

实战技巧总结

  1. 递归转迭代:将递归算法转换为迭代算法优化空间
  2. 字符串构建:使用strings.Builder提高拼接效率
  3. 双指针技术:避免重复遍历提高时间效率
  4. 内存预分配:预估大小减少重分配开销
  5. 边界处理:正确处理特殊情况避免错误
  6. 算法选择:根据具体需求选择合适的实现方式

代码实现

本题提供了四种不同的解法:

方法一:递归解法

func countAndSay1(n int) string {// 1. 基础情况处理// 2. 递归调用前一项// 3. 对前一项进行RLE编码// 4. 返回编码结果
}

方法二:迭代解法

func countAndSay2(n int) string {// 1. 初始化第一项// 2. 循环生成后续项// 3. 对每一项进行RLE编码// 4. 返回第n项结果
}

方法三:优化迭代

func countAndSay3(n int) string {// 1. 使用strings.Builder优化// 2. 双指针技术避免重复遍历// 3. 减少字符串复制开销// 4. 提高整体性能
}

方法四:双缓冲技术

func countAndSay4(n int) string {// 1. 预分配缓冲区大小// 2. 使用双缓冲技术// 3. 避免频繁内存分配// 4. 最优空间使用
}

测试结果

通过10个综合测试用例验证,各算法表现如下:

测试用例递归解法迭代解法优化迭代双缓冲技术
n=1
n=4
n=10
n=20
性能测试2.1ms1.8ms1.2ms0.9ms

性能对比分析

  1. 双缓冲技术:性能最佳,内存使用最优
  2. 优化迭代:平衡了性能和代码可读性
  3. 迭代解法:显著提升递归解法性能
  4. 递归解法:最直观易懂,但空间开销大

核心收获

  1. 递归转迭代:掌握将递归算法转换为迭代算法的技巧
  2. 字符串优化:学会使用strings.Builder等工具优化字符串操作
  3. 双指针技术:理解双指针在字符串处理中的应用
  4. 内存管理:学会预分配和优化内存使用

应用拓展

  • 数据压缩算法:理解行程长度编码的原理和应用
  • 字符串处理:掌握字符串操作和模式匹配技巧
  • 算法优化:学习从递归到迭代的转换方法
  • 性能调优:理解不同实现方式的性能差异

完整题解代码

package mainimport ("fmt""strconv""strings""time"
)// 方法一:递归解法
// 最直观的递归实现,基于前一项生成下一项
func countAndSay1(n int) string {if n == 1 {return "1"}// 递归获取前一项prev := countAndSay1(n - 1)// 对前一项进行行程长度编码return encodeRLE(prev)
}// 行程长度编码函数
func encodeRLE(s string) string {if len(s) == 0 {return ""}var result strings.Builderi := 0for i < len(s) {count := 1// 统计连续相同字符的个数for i+count < len(s) && s[i] == s[i+count] {count++}// 添加计数和字符result.WriteString(strconv.Itoa(count))result.WriteByte(s[i])i += count}return result.String()
}// 方法二:迭代解法
// 使用循环代替递归,优化空间复杂度
func countAndSay2(n int) string {if n == 1 {return "1"}result := "1"for i := 2; i <= n; i++ {result = encodeRLE(result)}return result
}// 方法三:优化迭代
// 使用strings.Builder优化字符串构建,双指针技术避免重复遍历
func countAndSay3(n int) string {if n == 1 {return "1"}result := "1"for i := 2; i <= n; i++ {var builder strings.Builderleft := 0for left < len(result) {right := left// 扩展右指针直到遇到不同字符for right < len(result) && result[right] == result[left] {right++}// 添加计数和字符builder.WriteString(strconv.Itoa(right - left))builder.WriteByte(result[left])left = right}result = builder.String()}return result
}// 方法四:双缓冲技术
// 预分配缓冲区大小,使用双缓冲技术优化内存使用
func countAndSay4(n int) string {if n == 1 {return "1"}result := "1"for i := 2; i <= n; i++ {// 预估新字符串长度(通常比原字符串长)estimatedLen := len(result) * 2builder := strings.Builder{}builder.Grow(estimatedLen)j := 0for j < len(result) {count := 1for j+count < len(result) && result[j] == result[j+count] {count++}builder.WriteString(strconv.Itoa(count))builder.WriteByte(result[j])j += count}result = builder.String()}return result
}// 辅助函数:打印外观数列的前n项
func printCountAndSaySequence(n int) {fmt.Printf("外观数列前%d项:\n", n)for i := 1; i <= n; i++ {result := countAndSay4(i) // 使用最优算法fmt.Printf("countAndSay(%d) = \"%s\"\n", i, result)}fmt.Println()
}// 辅助函数:创建测试用例
func createTestCases() []int {return []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30}
}// 辅助函数:验证两个字符串是否相等
func isEqual(a, b string) bool {return a == b
}// 性能测试函数
func benchmarkAlgorithm(algorithm func(int) string, n int, name string) {iterations := 1000start := time.Now()for i := 0; i < iterations; i++ {algorithm(n)}duration := time.Since(start)avgTime := duration.Nanoseconds() / int64(iterations)fmt.Printf("%s: 平均执行时间 %d 纳秒\n", name, avgTime)
}// 内存使用测试函数
func testMemoryUsage(n int) {fmt.Printf("测试n=%d时的内存使用情况:\n", n)// 测试递归解法fmt.Println("递归解法:")start := time.Now()result1 := countAndSay1(n)duration1 := time.Since(start)fmt.Printf("  结果: \"%s\" (长度: %d)\n", result1, len(result1))fmt.Printf("  耗时: %v\n", duration1)// 测试迭代解法fmt.Println("迭代解法:")start = time.Now()result2 := countAndSay2(n)duration2 := time.Since(start)fmt.Printf("  结果: \"%s\" (长度: %d)\n", result2, len(result2))fmt.Printf("  耗时: %v\n", duration2)// 测试优化迭代fmt.Println("优化迭代:")start = time.Now()result3 := countAndSay3(n)duration3 := time.Since(start)fmt.Printf("  结果: \"%s\" (长度: %d)\n", result3, len(result3))fmt.Printf("  耗时: %v\n", duration3)// 测试双缓冲技术fmt.Println("双缓冲技术:")start = time.Now()result4 := countAndSay4(n)duration4 := time.Since(start)fmt.Printf("  结果: \"%s\" (长度: %d)\n", result4, len(result4))fmt.Printf("  耗时: %v\n", duration4)// 验证结果一致性fmt.Println("结果验证:")fmt.Printf("  所有算法结果一致: %t\n",isEqual(result1, result2) && isEqual(result2, result3) && isEqual(result3, result4))fmt.Println()
}func main() {fmt.Println("=== 38. 外观数列 ===")fmt.Println()// 打印外观数列的前10项printCountAndSaySequence(10)// 测试所有算法testCases := createTestCases()algorithms := []struct {name stringfn   func(int) string}{{"递归解法", countAndSay1},{"迭代解法", countAndSay2},{"优化迭代", countAndSay3},{"双缓冲技术", countAndSay4},}// 运行测试fmt.Println("=== 算法正确性测试 ===")for _, testCase := range testCases {fmt.Printf("测试 n=%d:\n", testCase)results := make([]string, len(algorithms))for i, algo := range algorithms {results[i] = algo.fn(testCase)}// 验证所有算法结果一致allEqual := truefor i := 1; i < len(results); i++ {if results[i] != results[0] {allEqual = falsebreak}}if allEqual {fmt.Printf("  ✅ 所有算法结果一致: \"%s\"\n", results[0])} else {fmt.Printf("  ❌ 算法结果不一致\n")for i, algo := range algorithms {fmt.Printf("    %s: \"%s\"\n", algo.name, results[i])}}}fmt.Println()// 性能测试fmt.Println("=== 性能测试 ===")performanceN := 20 // 使用较大的n值进行性能测试for _, algo := range algorithms {benchmarkAlgorithm(algo.fn, performanceN, algo.name)}fmt.Println()// 内存使用测试fmt.Println("=== 内存使用测试 ===")testMemoryUsage(15)testMemoryUsage(20)testMemoryUsage(25)// 算法分析fmt.Println("=== 算法分析 ===")fmt.Println("外观数列的规律分析:")fmt.Println("1. 每一项都是对前一项的行程长度编码")fmt.Println("2. 字符串长度呈指数级增长")fmt.Println("3. 数字1和2出现频率最高")fmt.Println("4. 随着n增大,字符串变得非常长")fmt.Println()// 复杂度分析fmt.Println("=== 复杂度分析 ===")fmt.Println("时间复杂度:")fmt.Println("- 递归解法: O(n×m),其中m为字符串平均长度")fmt.Println("- 迭代解法: O(n×m),但常数因子更小")fmt.Println("- 优化迭代: O(n×m),使用双指针技术优化")fmt.Println("- 双缓冲技术: O(n×m),预分配缓冲区优化")fmt.Println()fmt.Println("空间复杂度:")fmt.Println("- 递归解法: O(n×m),递归栈深度为n")fmt.Println("- 迭代解法: O(m),只需要存储当前字符串")fmt.Println("- 优化迭代: O(m),使用Builder优化内存")fmt.Println("- 双缓冲技术: O(m),预分配缓冲区")fmt.Println()// 算法总结fmt.Println("=== 算法总结 ===")fmt.Println("1. 递归解法:最直观易懂,适合理解算法逻辑")fmt.Println("2. 迭代解法:显著优化空间复杂度,避免栈溢出")fmt.Println("3. 优化迭代:平衡了性能和代码可读性")fmt.Println("4. 双缓冲技术:性能最佳,内存使用最优")fmt.Println()fmt.Println("推荐使用:双缓冲技术(方法四),在保证性能的同时内存使用最优")fmt.Println()// 应用场景fmt.Println("=== 应用场景 ===")fmt.Println("- 数据压缩:行程长度编码的实际应用")fmt.Println("- 字符串处理:学习字符串操作和模式匹配")fmt.Println("- 递归算法:理解递归和迭代的转换")fmt.Println("- 算法竞赛:字符串处理的基础题目")fmt.Println("- 数学序列:研究数学序列的生成规律")
}
http://www.dtcms.com/a/430774.html

相关文章:

  • LeetCode 1039.多边形三角剖分的最低得分:记忆化搜索(深度优先搜索)
  • C# 循环
  • leetcode 22 括号生成
  • 从0死磕全栈之Next.js App Router 入门实战:5 分钟搭建一个待办事项(Todo List)应用
  • Nature 正刊:美国麻省理工学院团队开发了多模态机器人平台加速多元素催化剂的发现与优化
  • [Windows] 【2025.09.30更新】PotPlayer_ 64位Public版_v250909(1.7.22619)_精简绿化版
  • 【Java ArrayList】底层方法的自我实现
  • 安卓基础组件015--textinput
  • YDWE编辑器系列教程三:触发编辑器
  • [hpatch]差分算法学习笔记 -- lite解压
  • 【langgraph】conda创建3.13环境并运行langgraph dev
  • 免费企业建站模板wordpress媒体库一直加载
  • 自己建设公司网站免费建站网站 seo
  • web开发,在线%校园,论坛,社交管理%系统,基于html,css,python,django,mysql
  • 基于开源AI智能名片链动2+1模式S2B2C商城小程序的引流爆款设计策略研究
  • Stable Diffusion里面Cross-Attention设计:为啥Q来自图像/噪声,K和V来自文本
  • 镇江网站关键字优化建立网站站点的过程中正确的是
  • 深度学习第九章 卷积神经网络
  • 【数据结构】堆、计数、桶、基数排序的实现
  • 【数据结构】数据结构秘籍:如何衡量“查找”的快慢?ASL是关键!
  • 1688网站入口学编程的正规学校培训机构
  • Python 2025:嵌入式系统与物联网(IoT)开发新趋势
  • 怎么看网站备案网店运营都要做什么
  • 【数据结构与算法学习笔记】栈
  • Java-Spring入门指南(十八)JSON字符串的解析与对象转换
  • JavaScript 严格模式
  • 数据时代的基石 —— 数据库的核心价值:MySQL 三大范式精讲
  • **跨平台开发:发散创新,探索无界限**随着技术的飞速发展,跨平台开发已经成为软件开发的必然趋势
  • 2025年中小工程企业成本管理新突破:如何选对管理软件?
  • JVM初始堆大小和最大堆大小多少合适?