【前端算法】字符串中连续最多的字符以及次数

简介: 双指针与双层循环“跳步”的比较

字符串中连续最多的字符以及次数

  • 如, 输入 ‘ abbcccddeeee1234’ , 计算得到:
  • 连续最多的字符是 'e' , 4 次

传统思路

  • 嵌套循环,找出每个字符的连接次数,并记录
  • 看似时间复杂度是O(n^2)
  • 但实际时间复杂度是多少?——O(n),因为有“跳步”

代码实现

export interface IRes {
  curChar: string
  length: number
}

export function findContinousChar1 (str:string):IRes {
  const res:IRes = {
    curChar: '',
    length: 0
  }

  const len = str.length
  if (len === 0) return res

  let charLength = 0 // 记录当前连续长度最大的字符长度
  // O(n)
  for (let i = 0; i < len; i++) {
    charLength = 0 // 重置

    for (let j = i; j < len; j++) {
      if (str[i] === str[j]) {
        charLength++ // 连续相同 +1
      }

      if (str[i] !== str[j] || j === len -1) {
        // 不相等或者已经到了最后一个字符
        if (charLength > res.length) {
          res.curChar = str[i]
          res.length = charLength
        }

        if (i < len -1) {
          i = j -1 // 跳步,让i直接跳到j的当前位置,继续下一步的循环
        }

        break
      }      
    }
  }

  return res
}

功能测试

const str = "abbcccddeeee1234"
const res = findContinousChar1(str)
console.info(res)

打印结果

{ curChar: 'e', length: 4 }

单元测试

describe('查找连续字符最大长度',() => {
  it('正常情况', () => {
    const str = "abbcccddeeee1234"
    const res = findContinousChar1(str)
    expect(res).toEqual({ curChar: 'e', length: 4 })

  })

  it('空字符串', () => {
    const res = findContinousChar1('')
    expect(res).toEqual({ curChar: '', length: 0 })
  })

  it('非连续字符串', () => {
    const str = "abc"
    const res = findContinousChar1(str)
    expect(res).toEqual({ curChar: 'a', length: 1 })
  })

  it('连续字符串', () => {
    const str = "aaa"
    const res = findContinousChar1(str)
    expect(res).toEqual({ curChar: 'a', length: 3 })
  })
})

优化——双指针思想

  • 定义指针i 和 j 。j不动, i 就行移动
  • 如果i 和 j的值一直相等,则i继续移动
  • 直到i和j的值不相等,记录处理,让j追上i。继续新一轮的计算

代码实现

export function findContinousChar2 (str:string):IRes {
  const res:IRes = {
    curChar: '',
    length: 0
  }

  const len = str.length
  if (len === 0) return res

  let charLength = 0 // 记录当前连续长度最大的字符长度

  let i = 0
  let j = 0

  // O(n)
 for (;i < len ; i++) {
  if (str[i] === str[j]) {
    charLength++
  }

  if (str[i] !== str[j] || i === len -1) {
    // 不相等或者已经到了最后一个字符
    if (res.length < charLength) {
      res.curChar = str[j]
      res.length = charLength
    }

    charLength = 0 // 重置

    if (i < len -1) {
      j = i // 让j “追上” i,开始新一轮的计算
      i-- // 细节,让i和j保存新一轮的同步
    }
  }
  
 }

功能测试

const str = "abbcccddeeee1234"
const res = findContinousChar2(str)
console.info(res)

打印结果

{ curChar: 'e', length: 4 }

单元测试

describe('查找连续字符最大长度',() => {
  it('正常情况', () => {
    const str = "abbcccddeeee1234"
    const res = findContinousChar2(str)
    expect(res).toEqual({ curChar: 'e', length: 4 })

  })

  it('空字符串', () => {
    const res = findContinousChar2('')
    expect(res).toEqual({ curChar: '', length: 0 })
  })

  it('非连续字符串', () => {
    const str = "abc"
    const res = findContinousChar2(str)
    expect(res).toEqual({ curChar: 'a', length: 1 })
  })

  it('连续字符串', () => {
    const str = "aaa"
    const res = findContinousChar2(str)
    expect(res).toEqual({ curChar: 'a', length: 3 })
  })
})

性能比较

let str1 = ''
for (let i = 0; i < 100 * 10000; i++) {
 str1 += i.toString()
}

console.time('findContinousChar1')
findContinousChar1(str1)
console.timeEnd('findContinousChar1')

console.time('findContinousChar2')
findContinousChar2(str1)
console.timeEnd('findContinousChar2')

打印结果

findContinousChar1: 128.688ms
findContinousChar2: 120.392ms

两个函数执行时间相差无几,由此可见这里的嵌套循环的复杂度确实是O(n) 而不是 O(n ^ 2), 不过双指针在代码层次可以较清晰的计算出时间复杂度。

总结

  • 要注意实际复杂度,不要被代码表面迷惑
  • 双指针常用于解决嵌套循环
  • 算法题慎用正则表达式(实际工作可以用)
相关文章
|
1月前
|
算法 前端开发 数据处理
小白学python-深入解析一位字符判定算法
小白学python-深入解析一位字符判定算法
47 0
|
3月前
|
搜索推荐 前端开发 数据可视化
【优秀python web毕设案例】基于协同过滤算法的酒店推荐系统,django框架+bootstrap前端+echarts可视化,有后台有爬虫
本文介绍了一个基于Django框架、协同过滤算法、ECharts数据可视化以及Bootstrap前端技术的酒店推荐系统,该系统通过用户行为分析和推荐算法优化,提供个性化的酒店推荐和直观的数据展示,以提升用户体验。
155 1
|
3月前
|
算法
【算法】位运算算法——判断字符是否唯一
【算法】位运算算法——判断字符是否唯一
|
3月前
|
算法
【算法】滑动窗口——找到字符串中所有字母异位词
【算法】滑动窗口——找到字符串中所有字母异位词
|
3月前
|
算法
【算法】滑动窗口——无重复字符的最长子串
【算法】滑动窗口——无重复字符的最长子串
|
1月前
|
算法
两个字符串匹配出最长公共子序列算法
本文介绍了最长公共子序列(LCS)问题的算法实现,通过动态规划方法求解两个字符串的最长公共子序列,并提供了具体的编程实现细节和示例。
80 1
两个字符串匹配出最长公共子序列算法
|
1月前
|
移动开发 算法 前端开发
前端常用算法全解:特征梳理、复杂度比较、分类解读与示例展示
前端常用算法全解:特征梳理、复杂度比较、分类解读与示例展示
22 0
|
1月前
|
前端开发 JavaScript 安全
前端JS实现密码校验键盘横竖、26字母、相同字母、相同数字、密码包含用户名、数字 字母不能连续 不能相同三个、不能横向 竖向 连续三个 包含字符、不能有中文符号
该 JavaScript 代码实现了一个严格的密码校验功能,确保密码满足多种安全要求,包括长度、字符类型、不包含中文及特殊字符、不与用户名相似等。通过多个辅助函数,如 `validateFormat` 检查密码格式,`isHasChinaCharFun` 检测中文符号,`getCharAll` 生成键盘组合,以及 `checkPasswordFun` 综合验证密码的有效性和安全性。此工具对于提高用户账户的安全性非常有用。
30 0
|
2月前
|
算法 前端开发 机器人
一文了解分而治之和动态规则算法在前端中的应用
该文章详细介绍了分而治之策略和动态规划算法在前端开发中的应用,并通过具体的例子和LeetCode题目解析来说明这两种算法的特点及使用场景。
一文了解分而治之和动态规则算法在前端中的应用
|
2月前
|
前端开发 JavaScript
前端基础(十五)_时间对象、字符串对象
本文介绍了JavaScript中时间对象的操作方法,包括获取和设置年、月、日、小时、分钟、秒等,以及如何格式化时间显示,同时提及了字符串对象的常用方法。
31 0
前端基础(十五)_时间对象、字符串对象