mirror of
https://gitlab.com/game-loader/hugo.git
synced 2025-04-20 05:52:07 +08:00
leetcode update
This commit is contained in:
parent
a073d1936f
commit
8d163be671
@ -9910,3 +9910,809 @@ func sortArray(nums []int) []int {
|
||||
return sortedArray
|
||||
}
|
||||
```
|
||||
|
||||
## day151 2024-07-26
|
||||
### 1334. Find the City With the Smallest Number of Neighbors at a Threshold Distance
|
||||
There are n cities numbered from 0 to n-1. Given the array edges where edges[i] = [fromi, toi, weighti] represents a bidirectional and weighted edge between cities fromi and toi, and given the integer distanceThreshold.
|
||||
|
||||
Return the city with the smallest number of cities that are reachable through some path and whose distance is at most distanceThreshold, If there are multiple such cities, return the city with the greatest number.
|
||||
|
||||
Notice that the distance of a path connecting cities i and j is equal to the sum of the edges' weights along that path.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题思路是比较明确的, 使用dijistra算法找到每个点到其他点的最短距离并保留在阈值距离以内的点, 比较每个点在阈值距离以内的点的数量, 找到点的数量最少的点并返回值最大的即可, 此处也可以使用Floyd算法来计算最短路径.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
type Edge struct {
|
||||
to int
|
||||
weight int
|
||||
}
|
||||
|
||||
type MinHeap struct {
|
||||
edges []Edge
|
||||
}
|
||||
|
||||
func (h *MinHeap) Len() int { return len(h.edges) }
|
||||
func (h *MinHeap) Less(i, j int) bool { return h.edges[i].weight < h.edges[j].weight }
|
||||
func (h *MinHeap) Swap(i, j int) { h.edges[i], h.edges[j] = h.edges[j], h.edges[i] }
|
||||
|
||||
func (h *MinHeap) Push(x interface{}) {
|
||||
h.edges = append(h.edges, x.(Edge))
|
||||
}
|
||||
|
||||
func (h *MinHeap) Pop() interface{} {
|
||||
old := h.edges
|
||||
n := len(old)
|
||||
x := old[n-1]
|
||||
h.edges = old[0 : n-1]
|
||||
return x
|
||||
}
|
||||
|
||||
func dijkstra(n int, edges [][]int, start int, distanceThreshold int) []int {
|
||||
graph := make([][]Edge, n)
|
||||
for _, edge := range edges {
|
||||
graph[edge[0]] = append(graph[edge[0]], Edge{edge[1], edge[2]})
|
||||
graph[edge[1]] = append(graph[edge[1]], Edge{edge[0], edge[2]})
|
||||
}
|
||||
|
||||
dist := make([]int, n)
|
||||
for i := range dist {
|
||||
dist[i] = math.MaxInt32
|
||||
}
|
||||
dist[start] = 0
|
||||
|
||||
h := &MinHeap{}
|
||||
heap.Push(h, Edge{start, 0})
|
||||
|
||||
for h.Len() > 0 {
|
||||
current := heap.Pop(h).(Edge)
|
||||
currentCity := current.to
|
||||
currentDistance := current.weight
|
||||
|
||||
if currentDistance > distanceThreshold {
|
||||
break
|
||||
}
|
||||
|
||||
for _, edge := range graph[currentCity] {
|
||||
if newDist := currentDistance + edge.weight; newDist < dist[edge.to] {
|
||||
dist[edge.to] = newDist
|
||||
heap.Push(h, Edge{edge.to, newDist})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dist
|
||||
}
|
||||
|
||||
func findTheCity(n int, edges [][]int, distanceThreshold int) int {
|
||||
minReachable := n + 1
|
||||
cityWithMinReachable := -1
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
distance := dijkstra(n, edges, i, distanceThreshold)
|
||||
reachableCount := 0
|
||||
|
||||
for _, d := range distance {
|
||||
if d <= distanceThreshold {
|
||||
reachableCount++
|
||||
}
|
||||
}
|
||||
|
||||
if reachableCount < minReachable || (reachableCount == minReachable && i > cityWithMinReachable) {
|
||||
minReachable = reachableCount
|
||||
cityWithMinReachable = i
|
||||
}
|
||||
}
|
||||
|
||||
return cityWithMinReachable
|
||||
}
|
||||
```
|
||||
|
||||
## day152 2024-07-27
|
||||
### 2976. Minimum Cost to Convert String I
|
||||
You are given two 0-indexed strings source and target, both of length n and consisting of lowercase English letters. You are also given two 0-indexed character arrays original and changed, and an integer array cost, where cost[i] represents the cost of changing the character original[i] to the character changed[i].
|
||||
|
||||
You start with the string source. In one operation, you can pick a character x from the string and change it to the character y at a cost of z if there exists any index j such that cost[j] == z, original[j] == x, and changed[j] == y.
|
||||
|
||||
Return the minimum cost to convert the string source to the string target using any number of operations. If it is impossible to convert source to target, return -1.
|
||||
|
||||
Note that there may exist indices i, j such that original[j] == original[i] and changed[j] == changed[i].
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题和昨天的题目核心思路上基本相同, 只需要遍历原始字母和其映射字母以及相应的花费, 将字母对应的映射字母和其花费保存起来, 这里可以使用一个map, 也可以把字母当成下标, 用一个26*26的数组来保存(只有小写字母). 关键还是当原始字母和映射字母均相同时如果有多个不同的花费, 要将最小的花费保存下来. 因为这里我们只关心最小花费, 每个映射都是最小花费最终得到的即为最小花费, 是一个比较清晰的贪心法. 需要注意的是可能存在c->e, e->b这样的转换的花费比c->b小得多, 是不是感觉很熟悉, 实际上这个问题完全可以把字母当成节点, 花费当成边的花费, 这样问题就转换成了和昨天完全一样的问题. 使用同样的方法求解即可. 本题中求解最短路径我们使用Floyd-Warshall算法, 该算法的原理为动态规划.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func minimumCost(source string, target string, original []byte, changed []byte, cost []int) int64 {
|
||||
costs := make([][]int, 26)
|
||||
for i,_ := range costs{
|
||||
costs[i] = make([]int, 26)
|
||||
for j:= range costs[i]{
|
||||
costs[i][j] = math.MaxInt32
|
||||
}
|
||||
costs[i][i] = 0
|
||||
}
|
||||
for i,_ := range original{
|
||||
costs[original[i]-'a'][changed[i]-'a'] = min(cost[i],costs[original[i]-'a'][changed[i]-'a'])
|
||||
}
|
||||
for k:=0;k<26;k++{
|
||||
for i:=0;i<26;i++{
|
||||
for j:=0;j<26;j++{
|
||||
if costs[i][j] > costs[i][k]+costs[k][j] {
|
||||
costs[i][j] = costs[i][k] + costs[k][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result := 0
|
||||
for i := range source{
|
||||
if costs[source[i]-'a'][target[i]-'a'] == math.MaxInt32{
|
||||
return -1
|
||||
}else{
|
||||
result += costs[source[i]-'a'][target[i]-'a']
|
||||
}
|
||||
}
|
||||
return int64(result)
|
||||
}
|
||||
```
|
||||
|
||||
### 总结
|
||||
如果点特别多并且图为稀疏图, 用map来保存边的权重并执行Floyd算法效率会更高, 避免了不连通的边的遍历.
|
||||
|
||||
## day153 2024-07-28
|
||||
### 2045. Second Minimum Time to Reach Destination
|
||||
A city is represented as a bi-directional connected graph with n vertices where each vertex is labeled from 1 to n (inclusive). The edges in the graph are represented as a 2D integer array edges, where each edges[i] = [ui, vi] denotes a bi-directional edge between vertex ui and vertex vi. Every vertex pair is connected by at most one edge, and no vertex has an edge to itself. The time taken to traverse any edge is time minutes.
|
||||
|
||||
Each vertex has a traffic signal which changes its color from green to red and vice versa every change minutes. All signals change at the same time. You can enter a vertex at any time, but can leave a vertex only when the signal is green. You cannot wait at a vertex if the signal is green.
|
||||
|
||||
The second minimum value is defined as the smallest value strictly larger than the minimum value.
|
||||
|
||||
For example the second minimum value of [2, 3, 4] is 3, and the second minimum value of [2, 2, 4] is 4.
|
||||
Given n, edges, time, and change, return the second minimum time it will take to go from vertex 1 to vertex n.
|
||||
|
||||
Notes:
|
||||
|
||||
You can go through any vertex any number of times, including 1 and n.
|
||||
You can assume that when the journey starts, all signals have just turned green.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题是一道难度, 解决本题要将问题分解开. 首先考虑找到第二小时长到达目的地的路的问题, 因为经过每条路需要的时间都相同, 因此经过的节点数量与最终需要的时长成正比, 因此只需要找到经过节点第二多的路径即可. 注意当到达目的地的路径长度有多个不同值时, 直接存在第二长的路径, 而到达目的地的路径长度均相同时, 需要回退一步, 这种情况只需将最短路径经过的节点+2即得到第二长的路径. 另一个需要解决的问题是如何找到所有能到达目的地的路径, 这里可以使用bfs来解决, 使用bfs的好处在于不需要考虑只有一个长度的路径和不同长度的路径时不同的处理方式, 设置两个距离数组表示第一次访问到某节点需要的时间和第二次访问到某节点需要的时间, 只需要在bfs时不断填充这两个数组, 当某个节点在这两个数组中都存在值时, 说明该节点已经找到了最短路径和次短路径, 其余路径我们并不关心, 直接跳过该节点的其余处理即可.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func secondMinimum(n int, edges [][]int, time int, change int) int {
|
||||
g := make([][]int, n+1)
|
||||
for _, edge := range edges {
|
||||
u, v := edge[0], edge[1]
|
||||
g[u] = append(g[u], v)
|
||||
g[v] = append(g[v], u)
|
||||
}
|
||||
|
||||
dist1 := make([]int, n+1)
|
||||
dist2 := make([]int, n+1)
|
||||
for i := 1; i <= n; i++ {
|
||||
dist1[i] = -1
|
||||
dist2[i] = -1
|
||||
}
|
||||
dist1[1] = 0
|
||||
|
||||
q := list.New()
|
||||
q.PushBack([]interface{}{1, 1})
|
||||
|
||||
for q.Len() > 0 {
|
||||
front := q.Remove(q.Front()).([]interface{})
|
||||
x := front[0].(int)
|
||||
freq := front[1].(int)
|
||||
|
||||
t := dist1[x]
|
||||
if freq == 2 {
|
||||
t = dist2[x]
|
||||
}
|
||||
|
||||
if (t/change)%2 == 1 {
|
||||
t = (t/change + 1)*change + time
|
||||
} else {
|
||||
t += time
|
||||
}
|
||||
|
||||
for _, y := range g[x] {
|
||||
if dist1[y] == -1 {
|
||||
dist1[y] = t
|
||||
q.PushBack([]interface{}{y, 1})
|
||||
} else if dist2[y] == -1 && dist1[y] != t {
|
||||
if y == n {
|
||||
return t
|
||||
}
|
||||
dist2[y] = t
|
||||
q.PushBack([]interface{}{y, 2})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
## day154 2024-07-29
|
||||
### 1395. Count Number of Teams
|
||||
There are n soldiers standing in a line. Each soldier is assigned a unique rating value.
|
||||
|
||||
You have to form a team of 3 soldiers amongst them under the following rules:
|
||||
|
||||
Choose 3 soldiers with index (i, j, k) with rating (rating[i], rating[j], rating[k]).
|
||||
A team is valid if: (rating[i] < rating[j] < rating[k]) or (rating[i] > rating[j] > rating[k]) where (0 <= i < j < k < n).
|
||||
Return the number of teams you can form given the conditions. (soldiers can be part of multiple teams).
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
这种类型的问题核心在于找到通过保存什么样的状态, 可以简化枚举的过程, 避免重复枚举. 思考一下可知只要知道某个数前面和后面比该数大和比该数小的数字的个数, 即可得到以这个数为中间数的符合要求的三个数字的组合. 如某个数前面比它小(大)的数字有n个, 后面比它大(小)的数字有m个, 则以该数为中间数字满足条件的三数组合有m*n个. 依次遍历每个数字, 将数字作为三个数字中的中间数字来找到满足条件的组合, 这样寻找组合不会出现重复计数的问题. 因为每次用于固定的中间数字都不相同. 可以使用两个数组来保存所有位置前后比其大或者小的数字个数. 这里每个数字都要遍历一遍数组来找到前后比这个数字大或者小的数字个数, 总体时间复杂度为n^2.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func numTeams(rating []int) int {
|
||||
length := len(rating)
|
||||
type frontback struct{
|
||||
front int
|
||||
back int
|
||||
}
|
||||
small := make([]frontback, length)
|
||||
big := make([]frontback, length)
|
||||
for i:=1;i<length;i++{
|
||||
countsmall := 0
|
||||
countbig := 0
|
||||
for j:=0;j<i;j++{
|
||||
if rating[j] < rating[i]{
|
||||
countsmall++
|
||||
}else if rating[j] > rating[i]{
|
||||
countbig++
|
||||
}
|
||||
}
|
||||
small[i].front = countsmall
|
||||
big[i].front = countbig
|
||||
countsmall = 0
|
||||
countbig = 0
|
||||
for j:=i+1;j<length;j++{
|
||||
if rating[j] < rating[i]{
|
||||
countsmall++
|
||||
}else if rating[j] > rating[i]{
|
||||
countbig++
|
||||
}
|
||||
}
|
||||
small[i].back = countsmall
|
||||
big[i].back = countbig
|
||||
}
|
||||
result := 0
|
||||
for i:=1;i<length;i++{
|
||||
result += small[i].front * big[i].back
|
||||
result += small[i].back * big[i].front
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
## day155 2024-07-30
|
||||
### 1653. Minimum Deletions to Make String Balanced
|
||||
You are given a string s consisting only of characters 'a' and 'b'.
|
||||
|
||||
You can delete any number of characters in s to make s balanced. s is balanced if there is no pair of indices (i,j) such that i < j and s[i] = 'b' and s[j]= 'a'.
|
||||
|
||||
Return the minimum number of deletions needed to make s balanced.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题想实现题中所述的平衡字符串, 需要在前面删掉一定数量的b, 后面删掉一定数量的a以满足题目要求的b都在a的后面的条件, 则先统计字符串中a和b的数量, 再进行计算, 每次遇到b时计算该b前面需要删除的b的个数和该b后面的a的个数并加和作为需要删除的字符总数. 极端情况为当第一次遇到b且b后存在a时, 相当于删掉所有b后的a. 对于ababa这样的字符串, 第一次遇到b我们计算的是删掉后面的两个a, 第二次遇到b我们计算的是第一个b和第二个b后面的a. 通过这种方式遍历数组并计算需要删掉的字符个数即可.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func minimumDeletions(s string) int {
|
||||
counta := 0
|
||||
prea := 0
|
||||
delb := 0
|
||||
preb := 0
|
||||
countb := 0
|
||||
for i := range s{
|
||||
if s[i] == 'a'{
|
||||
counta++
|
||||
}else if s[i] == 'b'{
|
||||
countb++
|
||||
}
|
||||
}
|
||||
result := 1000000
|
||||
for i := range s{
|
||||
if s[i] == 'a'{
|
||||
prea++
|
||||
delb += preb
|
||||
preb = 0
|
||||
}else if s[i] == 'b'{
|
||||
preb++
|
||||
result = min(result, counta - prea + delb)
|
||||
}
|
||||
}
|
||||
if result == 1000000{
|
||||
result = 0
|
||||
}
|
||||
result = min(result, countb)
|
||||
return result
|
||||
}
|
||||
```
|
||||
### 总结
|
||||
|
||||
这个思路还是有些复杂, 实际上只要不断保证局部最优最终就能得到全局最优, 而局部最优可以通过如下方式得到, 对于一个同时包含a和b的字符串, 如果a前面出现了b, 那么可以删掉b后面的a, 也可以删掉a前面的b, 但当b后面没有出现a时, b不需要被删除. 因此可以先将出现的b的次数保存, 当遇到a时再判断在之前已经删除的字符基础上再多删掉一个a和删掉前面的全部b哪种方式需要删除的字符个数更少.
|
||||
|
||||
```go
|
||||
func minimumDeletions(s string) int {
|
||||
var res, bCount int
|
||||
|
||||
for i := range s {
|
||||
if s[i] == 'a' {
|
||||
res = min(res+1, bCount)
|
||||
} else {
|
||||
bCount++
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
## day156 2024-07-31
|
||||
### 1105. Filling Bookcase Shelves
|
||||
You are given an array books where books[i] = [thicknessi, heighti] indicates the thickness and height of the ith book. You are also given an integer shelfWidth.
|
||||
|
||||
We want to place these books in order onto bookcase shelves that have a total width shelfWidth.
|
||||
|
||||
We choose some of the books to place on this shelf such that the sum of their thickness is less than or equal to shelfWidth, then build another level of the shelf of the bookcase so that the total height of the bookcase has increased by the maximum height of the books we just put down. We repeat this process until there are no more books to place.
|
||||
|
||||
Note that at each step of the above process, the order of the books we place is the same order as the given sequence of books.
|
||||
|
||||
For example, if we have an ordered list of 5 books, we might place the first and second book onto the first shelf, the third book on the second shelf, and the fourth and fifth book on the last shelf.
|
||||
Return the minimum possible height that the total bookshelf can be after placing shelves in this manner.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题是典型的动态规划的题目, 要求必须按顺序放置书, 则需要放置新的一本书时我们有两个选择. 1. 在之前放过书的最后一排继续放书 2. 开始在新的一排放书 这两种选择最终哪种会得到更好的结果是未知的. 假设f(i)为前i本书能得到的最小高度. 对于放置第j本书而言, 可以将其单独作为一排, 则总高度为第j本书的高度j+f(j-1). 也可以将其与前面的书一块放在同一排, 如将j和j-1本书放在同一排, 此时总高度为max(j,j-1)+f(j-2). 以此类推(max(j,j-1,j-2...)+f(j-4))直到多本书不能够放在同一排为止. 将已经计算过的f(i)保存起来可以减少重复运算, f(0)=0,且f(1)=books[0][1].
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func minHeightShelves(books [][]int, shelfWidth int) int {
|
||||
return arrangeBooks(books, shelfWidth)
|
||||
}
|
||||
|
||||
func arrangeBooks(books [][]int, maxShelfWidth int) int {
|
||||
minHeights := make([]int, len(books)+1)
|
||||
for i := range minHeights {
|
||||
minHeights[i] = math.MaxInt32
|
||||
}
|
||||
minHeights[0] = 0
|
||||
|
||||
for bookIndex := 1; bookIndex <= len(books); bookIndex++ {
|
||||
currentShelfHeight := 0
|
||||
currentShelfWidth := 0
|
||||
|
||||
for lastBook := bookIndex - 1; lastBook >= 0; lastBook-- {
|
||||
currentBookThickness := books[lastBook][0]
|
||||
currentBookHeight := books[lastBook][1]
|
||||
|
||||
if currentShelfWidth+currentBookThickness > maxShelfWidth {
|
||||
break
|
||||
}
|
||||
|
||||
currentShelfWidth += currentBookThickness
|
||||
currentShelfHeight = max(currentShelfHeight, currentBookHeight)
|
||||
|
||||
currentArrangementHeight := minHeights[lastBook] + currentShelfHeight
|
||||
minHeights[bookIndex] = min(minHeights[bookIndex], currentArrangementHeight)
|
||||
}
|
||||
}
|
||||
|
||||
return minHeights[len(books)]
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
## day157 2024-08-01
|
||||
### 2678. Number of Senior Citizens
|
||||
You are given a 0-indexed array of strings details. Each element of details provides information about a given passenger compressed into a string of length 15. The system is such that:
|
||||
|
||||
The first ten characters consist of the phone number of passengers.
|
||||
The next character denotes the gender of the person.
|
||||
The following two characters are used to indicate the age of the person.
|
||||
The last two characters determine the seat allotted to that person.
|
||||
Return the number of passengers who are strictly more than 60 years old.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题是一道简单题, 最基本的字符串操作, 一道只需要能把题读懂并且懂如何通过数组下标访问数组就能解决的问题, 通过下标找到年龄对应的两个字符并且将其转换为数字和60比较即可.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func countSeniors(details []string) int {
|
||||
result := 0
|
||||
for _, detail := range details{
|
||||
if (detail[11]-'0')*10+(detail[12]-'0') > 60{
|
||||
result++
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
### 12. Integer to Roman
|
||||
Seven different symbols represent Roman numerals with the following values:
|
||||
|
||||
Symbol Value
|
||||
I 1
|
||||
V 5
|
||||
X 10
|
||||
L 50
|
||||
C 100
|
||||
D 500
|
||||
M 1000
|
||||
Roman numerals are formed by appending the conversions of decimal place values from highest to lowest. Converting a decimal place value into a Roman numeral has the following rules:
|
||||
|
||||
If the value does not start with 4 or 9, select the symbol of the maximal value that can be subtracted from the input, append that symbol to the result, subtract its value, and convert the remainder to a Roman numeral.
|
||||
If the value starts with 4 or 9 use the subtractive form representing one symbol subtracted from the following symbol, for example, 4 is 1 (I) less than 5 (V): IV and 9 is 1 (I) less than 10 (X): IX. Only the following subtractive forms are used: 4 (IV), 9 (IX), 40 (XL), 90 (XC), 400 (CD) and 900 (CM).
|
||||
Only powers of 10 (I, X, C, M) can be appended consecutively at most 3 times to represent multiples of 10. You cannot append 5 (V), 50 (L), or 500 (D) multiple times. If you need to append a symbol 4 times use the subtractive form.
|
||||
Given an integer, convert it to a Roman numeral.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题主要也是读懂题意, 读懂题目分别获取数字千分位, 百分位, 十分位, 个位的数字, 并根据题目内容进行转换即可. 本题的巧妙之处在于可以将对应的字母不同的数字都列出来并从数字中按从大到小减去这些数字并加上对应的罗马字母, 这很像组合硬币的问题.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
import "strings"
|
||||
|
||||
func intToRoman(num int) string {
|
||||
values := []int{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
|
||||
symbols := []string{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}
|
||||
|
||||
var builder strings.Builder
|
||||
|
||||
for i := 0; num > 0; i++ {
|
||||
for num >= values[i] {
|
||||
builder.WriteString(symbols[i])
|
||||
num -= values[i]
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
```
|
||||
|
||||
## day158 2024-08-02
|
||||
### 2134. Minimum Swaps to Group All 1's Together II
|
||||
A swap is defined as taking two distinct positions in an array and swapping the values in them.
|
||||
|
||||
A circular array is defined as an array where we consider the first element and the last element to be adjacent.
|
||||
|
||||
Given a binary circular array nums, return the minimum number of swaps required to group all 1's present in the array together at any location.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题中每个数组中1的数量是固定的, 我们先计数数组中1的个数后只要找到包含1数量最多的长度为数组全部1的数量的子数组, 则此子数组中0的个数即为最少的交换次数. 如何找到这样的子数组呢, 对于一个给定的数组, 因为1的数量固定, 则要找到满足条件的长度固定的子数组, 可以使用滑动窗口, 窗口长度设置为1的数量, 随着窗口滑动不断更新窗口中0的个数的最小值. 从数组开头开始滑动直到数组结尾. 最终得到的最小值即为最少交换次数.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func minSwaps(nums []int) int {
|
||||
countone := 0
|
||||
for _,num := range nums{
|
||||
if num == 1{
|
||||
countone++
|
||||
}
|
||||
}
|
||||
length := len(nums)
|
||||
min0 := 0
|
||||
current0 := 0
|
||||
left := 0
|
||||
right := countone-1
|
||||
for _,num := range nums[0:countone]{
|
||||
if num == 0{
|
||||
min0++
|
||||
}
|
||||
}
|
||||
current0 = min0
|
||||
for left < length{
|
||||
if nums[(right+1)%length] == 0{
|
||||
current0++
|
||||
}
|
||||
if nums[left] == 0{
|
||||
current0--
|
||||
}
|
||||
min0 = min(current0, min0)
|
||||
left++
|
||||
right++
|
||||
}
|
||||
return min0
|
||||
}
|
||||
```
|
||||
|
||||
## day159 2024-08-03
|
||||
### 1460. Make Two Arrays Equal by Reversing Subarrays
|
||||
You are given two integer arrays of equal length target and arr. In one step, you can select any non-empty subarray of arr and reverse it. You are allowed to make any number of steps.
|
||||
|
||||
Return true if you can make arr equal to target or false otherwise.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题要抓住问题的核心, arr数组中可以对任意子数组进行翻转, 则实际上通过不同长度的数组翻转的组合可以实现交换数组中任意两个数的位置, 因为题目只要求判断arr能否通过子数组翻转变成target, 则只要两个数组中有相同数量的相同字母, 根据之前的结论对arr数组中任意两个数都可以交换的结论可知一定可以通过不断交换位置使得arr和target相同. 先遍历target数组, 保存数组中的字母及其对应的个数, 再遍历arr数组, 看是否字母及其个数均与target相同即得结果.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func canBeEqual(target []int, arr []int) bool {
|
||||
targets := make([]int, 1001)
|
||||
for _, num := range target{
|
||||
targets[num]++
|
||||
}
|
||||
for _,num := range arr{
|
||||
targets[num]--
|
||||
if targets[num] < 0{
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
### 13. Roman to Integer
|
||||
Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M.
|
||||
|
||||
Symbol Value
|
||||
I 1
|
||||
V 5
|
||||
X 10
|
||||
L 50
|
||||
C 100
|
||||
D 500
|
||||
M 1000
|
||||
For example, 2 is written as II in Roman numeral, just two ones added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II.
|
||||
|
||||
Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used:
|
||||
|
||||
I can be placed before V (5) and X (10) to make 4 and 9.
|
||||
X can be placed before L (50) and C (100) to make 40 and 90.
|
||||
C can be placed before D (500) and M (1000) to make 400 and 900.
|
||||
Given a roman numeral, convert it to an integer.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题是之前整数转换成罗马数字的题目的反向问题, 将罗马数字转换成整数, 则采取类似的思路, 只不过这次从后向前遍历罗马字符串并将对应的数字加到最终结果中, 用一个变量保存前一次加的值, 如果当前数字比前一次加上的值小则应减去当前数字, 最终得到结果.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func romanToInt(s string) int {
|
||||
romanMap := map[rune]int{
|
||||
'I': 1,
|
||||
'V': 5,
|
||||
'X': 10,
|
||||
'L': 50,
|
||||
'C': 100,
|
||||
'D': 500,
|
||||
'M': 1000,
|
||||
}
|
||||
|
||||
total := 0
|
||||
prevValue := 0
|
||||
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
currentValue := romanMap[rune(s[i])]
|
||||
|
||||
if currentValue < prevValue {
|
||||
total -= currentValue
|
||||
} else {
|
||||
total += currentValue
|
||||
}
|
||||
|
||||
prevValue = currentValue
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
```
|
||||
|
||||
## day160 2024-08-04
|
||||
### 1508. Range Sum of Sorted Subarray Sums
|
||||
You are given the array nums consisting of n positive integers. You computed the sum of all non-empty continuous subarrays from the array and then sorted them in non-decreasing order, creating a new array of n * (n + 1) / 2 numbers.
|
||||
|
||||
Return the sum of the numbers from index left to index right (indexed from 1), inclusive, in the new array. Since the answer can be a huge number return it modulo 109 + 7.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题先考虑计算子数组的和时, 如果固定子数组的开始下标, 不断扩展子数组的长度, 则这些子数组的和具有天然的递增顺序. 即可以通过固定子数组的开始下标得到以某下标开始的子数组和的递增序列. 遍历到以下一个数组下标开始的子数组并计算子数组和时, 相当于合并两个递增的数组, 将和不断插入到之前已经有序的数组中, 为了快速插入到之前的有序数组并且保持数组仍为有序数组, 可以使用优先级队列.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
type Pq []int
|
||||
func (pq Pq)Len() int{
|
||||
return len(pq)
|
||||
}
|
||||
|
||||
func (pq Pq) Less(i, j int)bool{
|
||||
return pq[i] < pq[j]
|
||||
}
|
||||
func (pq Pq) Swap(i, j int){
|
||||
pq[i],pq[j] = pq[j],pq[i]
|
||||
}
|
||||
func (pq *Pq) Pop() interface{} {
|
||||
n := len(*pq)
|
||||
item := (*pq)[n-1]
|
||||
(*pq) = (*pq)[0:n-1]
|
||||
return item
|
||||
}
|
||||
func (pq *Pq) Push(x interface{}) {
|
||||
*pq = append(*pq, x.(int))
|
||||
}
|
||||
|
||||
func rangeSum(nums []int, n int, left int, right int) int {
|
||||
pq := Pq{}
|
||||
sum := 0
|
||||
for j:=0;j<n;j++{
|
||||
sum += nums[j]
|
||||
pq = append(pq, sum)
|
||||
}
|
||||
heap.Init(&pq)
|
||||
for i:=1;i<n;i++{
|
||||
sum = 0
|
||||
for j:=i;j<n;j++{
|
||||
sum += nums[j]
|
||||
heap.Push(&pq, sum)
|
||||
}
|
||||
}
|
||||
for i:=1;i<left;i++{
|
||||
heap.Pop(&pq)
|
||||
}
|
||||
result := 0
|
||||
for left<=right{
|
||||
result += heap.Pop(&pq).(int)
|
||||
left++
|
||||
}
|
||||
return result % (1000000007)
|
||||
}
|
||||
```
|
||||
|
||||
### 总结
|
||||
这种解法时间复杂度很高, 可以使用前缀和的前缀和求解, 这种思路比较复杂, 推荐看官方题解
|
||||
|
||||
[官方题解](https://leetcode.cn/problems/range-sum-of-sorted-subarray-sums/solutions/371273/zi-shu-zu-he-pai-xu-hou-de-qu-jian-he-by-leetcode-/)
|
||||
|
||||
## day161 2024-08-05
|
||||
### 2053. Kth Distinct String in an Array
|
||||
A distinct string is a string that is present only once in an array.
|
||||
|
||||
Given an array of strings arr, and an integer k, return the kth distinct string present in arr. If there are fewer than k distinct strings, return an empty string "".
|
||||
|
||||
Note that the strings are considered in the order in which they appear in the array.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题是简单题, 先遍历数组并使用map保存字符串及其对应的出现次数, 再遍历数组找到只出现过一次的第k个字符串即可, 如没有这样的字符串返回空字符串.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func kthDistinct(arr []string, k int) string {
|
||||
maps := map[string]int{}
|
||||
for _, str := range arr{
|
||||
maps[str] = maps[str] + 1
|
||||
}
|
||||
count := 0
|
||||
for _, str := range arr{
|
||||
if maps[str] == 1{
|
||||
count++
|
||||
}
|
||||
if count == k{
|
||||
return str
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
```
|
||||
### 6. Zigzag Conversion
|
||||
The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
|
||||
|
||||
P A H N
|
||||
A P L S I I G
|
||||
Y I R
|
||||
And then read line by line: "PAHNAPLSIIGYIR"
|
||||
|
||||
Write the code that will take a string and make this conversion given a number of rows:
|
||||
|
||||
string convert(string s, int numRows);
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题可以直接模拟z形排列的过程, 用一个二维数组来保存z形排列过程中每一行的字符串, 最后依次将每行字符串拼接起来即可. 模拟排列过程可以用两个变量, 一个控制排列的方向(从上到下或者从下到上), 一个控制当前的行数. 用strings.Builder可以快速拼接字符串.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func convert(s string, numRows int) string {
|
||||
if numRows == 1{
|
||||
return s
|
||||
}
|
||||
zig := make([]strings.Builder, numRows)
|
||||
direction := 0
|
||||
row := 0
|
||||
for _, char := range s{
|
||||
zig[row].WriteRune(char)
|
||||
if direction == 0{
|
||||
row++
|
||||
}
|
||||
if direction == 1{
|
||||
row--
|
||||
}
|
||||
if row == numRows-1{
|
||||
direction = 1
|
||||
}
|
||||
if row == 0{
|
||||
direction = 0
|
||||
}
|
||||
}
|
||||
var result strings.Builder
|
||||
for _, builder := range zig{
|
||||
result.WriteString(builder.String())
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
```
|
||||
|
||||
## day162 2024-08-06
|
||||
### 3016. Minimum Number of Pushes to Type Word II
|
||||
|
||||
You are given a string word containing lowercase English letters.
|
||||
|
||||
Telephone keypads have keys mapped with distinct collections of lowercase English letters, which can be used to form words by pushing them. For example, the key 2 is mapped with ["a","b","c"], we need to push the key one time to type "a", two times to type "b", and three times to type "c" .
|
||||
|
||||
It is allowed to remap the keys numbered 2 to 9 to distinct collections of letters. The keys can be remapped to any amount of letters, but each letter must be mapped to exactly one key. You need to find the minimum number of times the keys will be pushed to type the string word.
|
||||
|
||||
Return the minimum number of pushes needed to type word after remapping the keys.
|
||||
|
||||
An example mapping of letters to keys on a telephone keypad is given below. Note that 1, *, #, and 0 do not map to any letters.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
对于这种问题关键是把问题核心抽象出来, 本题中任意数量的字母可以映射在任意的电话按键上, 则采用类似哈夫曼编码的思想, 将出现频率最高的使其编码长度最短就能使总长度最短. 对应在本题中就是尽量让出现频率高的字母映射到按键的前面位置. 这样总的按键次数就会变少. 可用的按键总共有8个, 则先统计字符串中所有字母出现的频率并从大到小排序, 随后8个为一组进行映射, 如将频率前8的字母映射到8个按键的第一位上, 以此类推. 设组数为k, 将当前组中字母出现的次数与k相乘, 并将这些相加, 得到最终结果. 题目只要求最终的按键次数总和, 因此可以直接用一个数组, 下标表示26个小写字母, 对应的项表示出现频数, 将该数组排序后, 按照之前的思路每8个一组与1,2,3,4相乘即得最终结果.
|
||||
|
||||
### 代码
|
||||
```go
|
||||
func minimumPushes(word string) int {
|
||||
chars := make([]int, 26)
|
||||
for i,_ := range word{
|
||||
chars[word[i] - 'a']++
|
||||
}
|
||||
sort.Ints(chars)
|
||||
result := 0
|
||||
for i,_ := range chars{
|
||||
result += chars[25-i] * (i/8+1)
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user