2021-11-24:把一个01字符串切成多个部分,要求每一部分的0和1比例一样,同时要求尽可能多的划分, 比如 : 01010101, 01 01 01 01 这是一种切法,0和1比例为 1 : 1, 0101 0101 也是一种切法,0和1比例为 1 : 1, 两种切法都符合要求,但是那么尽可能多的划分为第一种切法,部分数为4, 比如 : 00001111, 只有一种切法就是00001111整体作为一块,那么尽可能多的划分,部分数为1, 给定一个01字符串str,假设长度为N,要求返回一个长度为N的数组ans, 其中ans[i] = str[0...i]这个前缀串,要求每一部分的0和1比例一样,同时要求尽可能多的划分下,部分数是多少? 输入: str = "010100001", 输出: ans = [1, 1, 1, 2, 1, 2, 1, 1, 3]。 来自京东。
答案2021-11-24:
考点是分数表示,保证没有精度损失。 1.分数表示。 分子是0的个数,分母是1的个数。 key是分子/分母。在go语言中,用结构体表示分数。 value是个数。 2.如果整体的分数和局部的分数一样,那么整体的个数一定加1。 时间复杂度:O((N)。 空间复杂度:O(N)。
代码用golang编写。代码如下:
package main
import "fmt"
func main() {
arr := []int{0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0}
ret := split(arr)
fmt.Println("两个变量表示分数:", ret)
ret = split2(arr)
fmt.Println("结构体表示分数:", ret)
}
type r struct {
a int
b int
}
func NewR(a int, b int) r {
res := r{}
g := gcd(a, b)
res.a = a / g
res.b = b / g
return res
}
func split2(arr []int) []int {
// key : 分子
// value : 属于key的分母表, 每一个分母,及其 分子/分母 这个比例,多少个前缀拥有
pre := make(map[r]int)
n := len(arr)
ans := make([]int, n)
zero := 0 // 0出现的次数
one := 0 // 1出现的次数
for i := 0; i < n; i++ {
if arr[i] == 0 {
zero++
} else {
one++
}
if zero == 0 || one == 0 {
ans[i] = i + 1
} else { // 0和1,都有数量 -> 最简分数
pre[NewR(zero, one)]++
ans[i] = pre[NewR(zero, one)]
}
}
return ans
}
// 001010010100...
func split(arr []int) []int {
// key : 分子
// value : 属于key的分母表, 每一个分母,及其 分子/分母 这个比例,多少个前缀拥有
//HashMap<Integer, HashMap<Integer, Integer>> pre = new HashMap<>();
pre := make(map[int]map[int]int)
n := len(arr)
ans := make([]int, n)
zero := 0 // 0出现的次数
one := 0 // 1出现的次数
for i := 0; i < n; i++ {
if arr[i] == 0 {
zero++
} else {
one++
}
if zero == 0 || one == 0 {
ans[i] = i + 1
} else { // 0和1,都有数量 -> 最简分数
gcd := gcd(zero, one)
a := zero / gcd
b := one / gcd
// a / b 比例,之前有多少前缀拥有? 3+1 4 5+1 6
if _, ok := pre[a]; !ok {
pre[a] = make(map[int]int)
}
if _, ok := pre[a][b]; !ok {
pre[a][b] = 1
} else {
pre[a][b] = pre[a][b] + 1
}
ans[i] = pre[a][b]
}
}
return ans
}
func gcd(m, n int) int {
if n == 0 {
return m
} else {
return gcd(n, m%n)
}
}
执行结果如下: