xxleyi / loop_invariants

记录以及整理「循环不变式」视角下的算法题解

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

leetcode 93. 复原IP地址

xxleyi opened this issue · comments

题:

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

示例:

输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/restore-ip-addresses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


解:此题又是一道典型的「回溯」类题目。

「回溯」的「循环不变式」不够明显:通过递归枚举所有可能。回退时,恢复现场;终止时,更新答案集

对于这道具体的题目,要通过耐心分析找到一个恰当的递归枚举方式,然后就是筛选出每一次递归枚举时的所有有效选择。

此题好玩的地方在于这个判断有效选择的条件比较繁琐,此时适合抽出布尔变量。

var restoreIpAddresses = function(s) {
  const ans = []
  const choices = []
  const l = s.length

  // 辅助函数
  function parseIP() {
    const IP = choices.join('')
    if (IP.split('.').every(e => parseInt(e) < 256)) ans.push(IP)
  }


  function backtrack(i = 0, pointCount = 0, last = -1, sliceLength = 0) {
    // 递归终止,尝试添加有效 IP
    if (pointCount === 3 && i === l) parseIP()
    // 越界直接返回,或许可以不需要,但放在这里更安全
    else if (pointCount > 3 || i > l) return
    // 回溯:递归 + 回退前清理现场
    else {
      // 一共两种可能
      const canPushNumber = (sliceLength > 0 && sliceLength < 3 && choices[last + 1 - sliceLength] != '0') || sliceLength === 0
      const canPushPoint = sliceLength > 0 && sliceLength <= 3

      // 分别对两种可能进行操作
      if (canPushNumber) {
        choices[last + 1] = s[i]
        backtrack(i + 1, pointCount, last + 1, sliceLength + 1)
      }
      if (canPushPoint) {
        choices[last + 1] = '.'
        backtrack(i, pointCount + 1, last + 1, 0)
      }
      // 回退之前清理现场
      choices.length = last + 1
    }
  }

  backtrack()
  return ans
};

进阶:在判断枚举出的 IP 是否有效时,可直接使用正则表达式