目录
IPv4地址转换成整数
考察进制转换、正则表达式
题目描述
存在一种虚拟IPv4地址,由4小节组成,每节的范围为0~255,以#号间隔,虚拟IPv4地址可以转换为一个32位的整数,例如: 128#0#255#255,转换为32位整数的结果为2147549183(0x8000FFFF) 1#0#0#0,转换为32位整数的结果为16777216(0x01000000) 现以字符串形式给出一个虚拟IPv4地址,限制第1小节的范围为1~128,即每一节范围分别为(1~128)#(0~255)#(0~255)#(0~255),要求每个IPv4地址只能对应到唯一的整数上。如果是非法IPv4,返回invalid IP
输入描述
输入一行,虚拟IPv4地址格式字符串
输出描述
输出以上,按照要求输出整型或者特定字符
备注
输入不能确保是合法的IPv4地址,需要对非法IPv4(空串,含有IP地址中不存在的字符,非合法的#分十进制,十进制整数不在合法区间内)进行识别,返回invalid IP
示例1
输入
100#101#1#5
输出
1684340997
解析
通过#号分隔后要为4位的数组,每个数要求都是整数且要满足大小要求,如果满足要求则将每位数乘以16的倍数转换为整数。
答案
function IPv4ToNumber(str) {
arr = str.split('#')
// 通过#号分隔后要为4位的数组,判断是否为无效IP格式
if (arr.length !== 4) {
return 'invalid IP'
}
// 每个数要求都是整数且满足大小要求,判断是否为无效IP格式
let isValid = arr.every((value, index) => {
let isInteger = value.match(/^\d+$/)
if (index === 0) {
if (isInteger && value <= 128 && value >= 1) {
return true
}
} else {
if (isInteger && value <= 255 && value >= 0) {
return true
}
}
return false
})
if (isValid) {
return arr.reduce((t, v, i) => t + v * Math.pow(16, 6 - 2 * i), 0)
}
return 'invalid IP'
}
console.log(IPv4ToNumber('100#101#1#5'))
console.log(IPv4ToNumber('128#0#255#255'))
关联子串
考察正则表达式,滑动窗口,hash。
题目描述
给定两个字符串 str1和 str2 如果字符串 str1 中的字符,经过排列组合后的字符串中,只要有一个字符串是str2的子串,则认为str1是str2的关联子串,若str1是str2的关联子串,请返回子串在str2的起始位置;若不是关联子串,则返回-1
预制条件
1.输入的字符串贝包含小写字母
2.两个字符串的长度范围 1 ~ 100000之前
3.若str2 中有多个str 的组合子串,请返回第一个子串的起始位置
示例一
输入
Str1 "abc", str2 "efghicabiii"
输出
5
解释
Str2包含str1的一种排列组合‘cab’,此组合在str2的字符串起始位置为5(从0开始计数)
示例二
输入
Str1 = ‘abc’,str2 = ‘efghicaibii’
输出
-1
解析
先通过正则匹配Str1中出现的字符锁定可能出现的位置,然后对每个位置用滑动窗口判断,当窗口中每个字符对应的个数相同时即找到了目标字符串。
答案
function relateStr(str1, str2) {
// 通过正则匹配str1中出现的字符串来缩小范围
let str1Reg = new RegExp(`[${str1}]+`, 'g')
arr = str2.match(str1Reg)
if (!arr) {
return -1
}
let resStr
// 用对象来记录目标字符串中每个字符出现的次数
let obj = {}
str1.split('').forEach(v => {
if (obj[v]) {
obj[v]++
} else {
obj[v] = 1
}
})
// 当第一个符合条件的满足时弹出
let hasStr = arr.some(v => {
let tmp = { ...obj }
// 长度不够直接返回
if (v.length < str1.length) {
return false
}
let start = 0;
for (let i = 0; i < v.length; i++) {
// 每向右走一步,需要的字符减1
let cur = v[i]
if (tmp[cur] > 0) {
tmp[cur]--
} else {
// 当字符为0表示不需要改字符了,左边的起点需要往前。
do {
tmp[str2[start]]++
start++
} while (tmp[cur] <= 0)
tmp[cur] = 0
}
// 长度满足要求表示所有的字符次数为0,即找到了目标字符串
if (i - start + 1 === str1.length) {
resStr = v.slice(start, i + 1)
return true
}
// 长度不够直接返回
if (v.length - start < str1.length) {
return false
}
}
})
if (hasStr) {
return str2.indexOf(resStr)
}
return -1
}
console.log(relateStr('abc', 'efghicabiii'))
console.log(relateStr('abc', 'efghicaibii'))
二叉树的广度优先遍历
考察广度优先算法、后序遍历和中序遍历
题目描述
有一棵二叉树,每个节点由一个大写字母标识(最多26个节点)。现有两组字母,分别表示后序遍历。(左孩子一右孩子→父节点)和中序遍历(左孩子→父节点一右孩子)的结果,请输出层序遍历的结果。
输入描述
输入为两个字符串,分别是二叉树的后序遍历和中序遍历的结果
输出描述
输出二叉树的层次遍历结果。
示例1
输入
CBEFDA CBAEDF
输出
ABDCEF
解析
1.通过后序和中序取出根节点。
2.将后序拆为左节点和右节点,中序拆为左节点和右节点。
3.后序的左节点和中序的左节点组为一组,后序的右节点和中序的右节点组为一组。
4.从3的两组中依次获取根节点后再拆分。
中间过程2个序列为一组,用队列实现广度优先算法,每次从队列尾部取出2个,得到根节点后把左节点的一组2个从头部加入,再把右节点的一组2个从头部加入。
答案
function BFS(str1, str2) {
let res = []
let tmp = [str1, str2]
//每次取出两字符串,得到根节点
while (tmp.length) {
let tmp2 = tmp.pop()
let tmp1 = tmp.pop()
// 得到根节点
let root = tmp1.slice(-1)
res.push(root)
// 中序中分出左节点和右节点
arr = tmp2.split(root)
let left = tmp1.slice(0, arr[0].length)
let right = tmp1.slice(arr[0].length, arr[0].length + arr[1].length)
if (left.length) {
// 中序的分出左节点,后序的左节点放入队列
tmp.unshift(left, arr[0])
}
if (right.length) {
// 中序的分出右节点,后序的右节点放入队列
tmp.unshift(right, arr[1])
}
}
return res.join('')
}
console.log(BFS('CBEFDA', 'CBAEDF'))