# 第 239 场周赛题解

# Q1 1848. 到目标元素的最小距离 (opens new window)

普通暴力。

class Solution1848 {
    fun getMinDistance(nums: IntArray, target: Int, start: Int): Int {
        var min = Int.MAX_VALUE
        for (i in nums.indices) {
            if (nums[i] == target) {
                min = minOf(min, abs(i - start))
            }
        }
        return min
    }
}
1
2
3
4
5
6
7
8
9
10
11

# Q2 1849. 将字符串拆分为递减的连续值 (opens new window)

DFSDFS向下查找,长度小于2020且至少要拆分为两个数值,因此使用LongLong足够表示。这里可以注意活用toLongOrNull()toLongOrNull()方法,可以方便的将不符合LongLong的数据排除,否则可能有RunTimeErrorRunTimeError

class Solution5747 {
    fun splitString(s: String): Boolean {
        fun dfs(left: Int, cur: Long): Boolean {
            if (left >= s.length) return true
            var ans = false
            for (i in left + 1..s.length) {
                val next = s.substring(left, i).toLongOrNull()
                if (next == cur - 1) {
                    ans = ans or dfs(i, next)
                }
            }
            return ans
        }
        for (i in 1 until s.length) {
            val cur = s.substring(0, i).toLongOrNull() ?: continue
            if (dfs(i, cur)) return true
        }
        return false
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Q3 1850. 邻位交换的最小次数 (opens new window)

本题的最终状态其实是求kknextPermutationnextPermutation的结果,主要还需要判断两个字符串需要交换多少次下标。交换的过程可以使用冒泡排序,查找交换次数的时间复杂度为O(n2)O(n^2)

class Solution1850 {
    fun getMinSwaps(num: String, k: Int): Int {
        val n = num.map { it - '0' }.toIntArray()
        for (i in 0 until k) {
            nextPermutation(n)
        }
        val start = num.map { it - '0' }.toIntArray()
        var ans = 0
        for (i in n.indices) {
            for (j in i until n.size) {
                if (start[i] == n[j]) {
                    ans += j - i
                    for (t in j - 1 downTo i) {
                        val tmp = n[t]
                        n[t] = n[t + 1]
                        n[t + 1] = tmp
                    }
                    break
                }
            }
        }
        return ans
    }

    fun nextPermutation(nums: IntArray) {
        for (i in nums.size - 2 downTo 0) {
            for (j in nums.size - 1 downTo i + 1) {
                if (nums[j] > nums[i]) {
                    val temp = nums[i]
                    nums[i] = nums[j]
                    nums[j] = temp
                    nums.sort(i + 1, nums.size)
                    return
                }
            }
        }
        nums.reverse()
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# Q4 1851. 包含每个查询的最小区间 (opens new window)

利用线段树模板!先将区间按照长度排序,然后依次更新线段树的值。最后查询一遍即可,注意线段树的mergemerge过程是要取最小值。

class Solution1851 {
    fun minInterval(intervals: Array<IntArray>, queries: IntArray): IntArray {
        val seg = SegmentTree<Int>(start = 0, end = Int.MAX_VALUE / 2, value = Int.MAX_VALUE / 2) { a, b -> minOf(a, b) }
        intervals.sortByDescending { it[1] - it[0] }
        intervals.forEach {
            seg.update(seg, it[0], it[1], it[1] - it[0] + 1)
        }
        val ans = arrayListOf<Int>()
        queries.forEach {
            seg.query(seg, it, it).also {
                if (it >= Int.MAX_VALUE / 2) ans.add(-1)
                else ans.add(it)
            }
        }
        return ans.toIntArray()
    }
}

class SegmentTree<T>(val start: Int = 0,
                     val end: Int = 0,
                     var value: T? = null,
                     private var lazy: T? = null,
                     val merge: (a: T, b: T) -> T) {

    var left: SegmentTree<T>? = null
    var right: SegmentTree<T>? = null
    val mid: Int
        get() {
            return start + (end - start) / 2
        }

    fun build(arr: Array<T>): SegmentTree<T>? {
        return buildHelper(0, arr.lastIndex, arr)
    }

    private fun buildHelper(left: Int, right: Int, arr: Array<T>): SegmentTree<T>? {
        if (left > right) return null
        val root = SegmentTree(left, right, arr[left], null, merge)
        if (left == right) return root
        val mid = (left + right) / 2
        root.left = buildHelper(left, mid, arr)
        root.right = buildHelper(mid + 1, right, arr)
        root.value = safeMerge(root.left?.value, root.right?.value)
        return root
    }

    private fun build(left: Int, right: Int, default: T?): SegmentTree<T>? {
        if (left > right) return null
        return SegmentTree(left, right, default, null, merge)
    }

    fun update(root: SegmentTree<T>, l: Int, r: Int, v: T) {
        if (l <= root.start && r >= root.end) {
            root.value = v
            root.lazy = safeMerge(root.lazy, v)
            return
        }
        // 动态开点线段树
        if (root.left == null || root.right == null) {
            val mid = root.mid
            if (root.left == null)
                root.left = build(root.start, mid, root.value)
            if (root.right == null)
                root.right = build(mid + 1, root.end, root.value)
        }
        pushDown(root)
        val mid = root.mid
        if (l <= mid) {
            update(root.left!!, l, r, v)
        }
        if (r > mid) {
            update(root.right!!, l, r, v)
        }
        root.value = merge(root.left!!.value!!, root.right!!.value!!)
    }

    private fun pushDown(root: SegmentTree<T>) {
        if (root.lazy == null) return
        root.left?.lazy = safeMerge(root.left?.lazy, root.lazy)
        root.right?.lazy = safeMerge(root.right?.lazy, root.lazy)
        root.left?.value = safeMerge(root.left?.value, root.lazy)
        root.right?.value = safeMerge(root.right?.value, root.lazy)
        root.lazy = null
    }

    fun update(root: SegmentTree<T>, index: Int, value: T) {
        if (root.start == index && root.end == index) {
            root.value = value
            return
        }
        // 动态开点线段树
        if (root.left == null || root.right == null) {
            val mid = root.mid
            if (root.left == null)
                root.left = build(root.start, mid, root.value)
            if (root.right == null)
                root.right = build(mid + 1, root.end, root.value)
        }
        val mid = root.mid
        if (index <= mid) {
            update(root.left!!, index, value)
            root.value = safeMerge(root.left!!.value, root.right!!.value)
        } else {
            update(root.right!!, index, value)
            root.value = safeMerge(root.left!!.value, root.right!!.value)
        }
    }

    fun query(root: SegmentTree<T>, left: Int, right: Int): T {
        if (left <= root.start && right >= root.end) {
            return root.value!!
        }
        // 动态开点线段树
        if (root.left == null || root.right == null) {
            val mid = root.mid
            if (root.left == null)
                root.left = build(root.start, mid, root.value)
            if (root.right == null)
                root.right = build(mid + 1, root.end, root.value)
        }
        pushDown(root)
        val mid = root.mid
        var ans: T? = null
        if (mid >= left) {
            ans = safeMerge(ans, query(root.left!!, left, right))
        }
        if (mid + 1 <= right) {
            ans = safeMerge(ans, query(root.right!!, left, right))
        }
        return ans!!
    }

    private fun safeMerge(a: T?, b: T?): T? {
        return when {
            a == null -> b
            b == null -> a
            else -> merge(a, b)
        }
    }

    // 注意lazy的更新策略
    private fun lazyMerge(a: T?, b: T?): T? {
        return b
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145