LeetCode / Scala - 无重复字符最长子串 ,最长回文子串

简介: ​LeetCode 里有一类字符子串问题,这里主要分析无重复字符的最长子串与最长回文子串,总结相关方法。

一.引言

LeetCode 里有一类字符子串问题,这里主要分析无重复字符的最长子串最长回文子串,总结相关方法。

二.无重复字符最长子串

1.题目要求

image.gif编辑

给定字符串 s,要求找出字符内无重复的最长子串,注意是子串不是子序列。

2.滑动指针法

A.思路分析

通过 start 与 end 双指针指定最长子串范围,通过 HashMap 记录每一个 char 字符的索引并实现去重。

-> 遍历字符串所有 char 字符

-> 判断当前字符是否在 HashMap 中,并更新当前 start

-> end += 1,并记录当前 char 对应的索引

-> length = end - start,比较 length 与 maxLength 并更新最大长度

B.代码实现

def solution(s: String): Int = {
    // 双指针与索引Map
    var maxLength = 0
    var start = 0
    var end = 0
    val indexMap = mutable.HashMap[Char, Int]()
    // 转换为char
    val arr = s.toCharArray
    s.toCharArray.foreach(char => {
      if (indexMap.contains(char)) {
        start = math.max(indexMap(char), start) // 更新 start
      }
      end += 1 // 更新 end
      indexMap(char) = end
      maxLength = math.max(end - start, maxLength) // 更新 maxLength
      println(s"char: $char start: $start end: $end max: $maxLength arr: ${s.slice(start, end).mkString("")}")
    })
    maxLength
  }

image.gif

为了直观,我们加入了日志打印,以 s = "abba" 为例进行测试:

def main(args: Array[String]): Unit = {
    val s = "abba"
    println(solution(s))
  }

image.gif

可以结合执行过程理解双指针 start 和 end 的变化。

image.gif编辑

C.执行效率

image.gif编辑

时间复杂度 O(n),其中 n 为字符串的长度,即 char 的数量,空间复杂度为 O(|∑|),其中 ∑ 为当前字符串的去重集合,正常情况可以近似为 O(n),极端情况例如 s="11111" 则退化为 O(1)。

三.最长回文子串

1.题目要求

image.gif编辑

给定字符串 s,寻找其最长回文子串,这里解释下什么是回文串,即正反一样的字符串,例如 aabbaa,aba,ccccc 均为回文子串,注意最长无重复子串返回字符串长度而本例返回字符串。

2.中心扩展法

A.思路分析

首先清晰回文字符的转移条件,假设 P(i,j) 为回文子串,如果 i-1,j+1 对应位置元素相同,则 P(i-1, j+1) 也是回文子串,如此类推

-> 初始化 maxLeft,maxRight 记录最长回文串的区间,初始化 maxLength 记录最长长度

-> 遍历数组数组索引,向左侧探索,当前索引为 mid,则左侧为 mid - 1,如果 s(mid) = s(left) 则 P(left, mid) 构成回文子串,left -=1 继续向左探索

-> 向右侧探索,方法同上,right = mid + 1,判断 s(mid) 与 s(right)

-> 向两侧扩展,单向扩展主要寻找与 mid 元素相同的元素,例如 a -> aaa,双侧则主要是寻找两侧相同元素,例如 aaa -> baaab,判断 s(left) = s(right)

-> 更新 maxLeft,maxRight 记录当前最长回文串范围

B.代码实现

// 中心扩展算法
  def solution(s: String): String = {
    var length = 1
    // 从每个 mid 开始向两边扩散
    var maxLeft = 0 // 起点
    var maxRight = 0 // 终点
    var maxLength = 0 // 最长回文串长度
    val arr = s.toCharArray
    arr.indices.foreach(mid => {
      var left = mid - 1
      var right = mid + 1
      // 向左侧扩展
      while (left >= 0 && arr(left).equals(arr(mid))) {
        left -= 1
        length += 1
      }
      // 向右侧扩展
      while (right <= arr.length - 1 && arr(right) == arr(mid)) {
        right += 1
        length += 1
      }
      // 向两侧扩展
      while (left >= 0 && right <= arr.length - 1 && arr(left) == arr(right)) {
        left -= 1
        right += 1
        length += 2
      }
      // 更新
      if (length > maxLength) {
        maxLeft = left
        maxRight = right
        maxLength = length
      }
      length = 1
    })
    println(maxLeft, maxRight, maxLength)
    s.substring(maxLeft + 1, maxRight)
  }

image.gif

分别向左,右,两侧扩展,最后更新区间,可以参照上面思路分析,由于判断相等后,继续 left -= 1 寻找下一个 left,所以最后满足的左边界为 realLeft = maxLeft + 1,maxRight 同理,maxRight = realRight + 1,所以最终结果 subString(maxLeft + 1,maxRight)。

C.执行效率

image.gif编辑

欧了一把,不知道这次提交怎么跑这么快,算法时间复杂度 O(n^2),因为遍历 n 个字符,每个字符左右扩展,空间复杂度 O(1)。

四.总结

可以看到很多算法都使用了 双指针、HashMap,因此要理解它们常用的用法与边界条件,其次回文串由于 P(i,j) 向 P(i-1,j+1) 的转移状态,也适用于动态规划,后面有时间整理一下动态规划相关。

目录
相关文章
|
4月前
Leetcode第五题(最长回文子串)
这篇文章介绍了解决LeetCode第五题“最长回文子串”问题的一种方法,使用了中心扩展法来寻找给定字符串中的最长回文子串。
50 0
|
4月前
|
存储 算法
Leetcode第三题(无重复字符的最长子串)
这篇文章介绍了解决LeetCode第三题“无重复字符的最长子串”的算法,使用滑动窗口技术来找出给定字符串中最长的不含重复字符的子串,并提供了详细的代码实现和解释。
145 0
Leetcode第三题(无重复字符的最长子串)
|
6月前
|
Python
【Leetcode刷题Python】5. 最长回文子串
LeetCode 5题 "最长回文子串" 的Python解决方案,使用动态规划算法找出给定字符串中的最长回文子串。
57 3
|
6月前
|
算法
LeetCode第5题最长回文子串
该文章介绍了 LeetCode 第 5 题最长回文子串的解法,通过分析回文子串的特点,使用动态规划的思想,用二维数组记录字符串是否为回文串,从后往前遍历找子串,减少重复判断,最终找到最长回文子串,并总结了解题时通过举例推导找规律的思路。
LeetCode第5题最长回文子串
|
6月前
|
算法
LeetCode第3题无重复字符的最长子串
该文章介绍了 LeetCode 第 3 题无重复字符的最长子串的解法,通过使用 HashSet 记录不重复的子元素,以每个字符开头遍历字符串,遇到重复字符则重新计算,最终找到最长子串,同时提到可以考虑使用 HashMap 降低复杂度。
LeetCode第3题无重复字符的最长子串
|
7月前
|
索引
821.字符的最短距离-力扣(LeetCode)
821.字符的最短距离-力扣(LeetCode)
60 0
|
8月前
|
存储 算法 程序员
力扣经典150题第三十一题:无重复字符的最长子串
力扣经典150题第三十一题:无重复字符的最长子串
47 0
|
8月前
【LeetCode刷题】滑动窗口思想解决问题:长度最小的子数组、无重复字符的最长子串
【LeetCode刷题】滑动窗口思想解决问题:长度最小的子数组、无重复字符的最长子串
|
5月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
6月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
146 2