牛客春招刷题训练营-2025.4.7题解

活动地址: 牛客春招刷题训练营 - 编程打卡活动

简单题 查找输入整数二进制中1的个数

方案一:使用 bits.OnesCount 函数

package main

import (
	"fmt"
	"math/bits"
)

func main() {
	var n, m uint
	fmt.Scan(&n, &m)
	fmt.Println(bits.OnesCount(n))
	fmt.Println(bits.OnesCount(m))
}

方案二:手动实现 countBits 函数

其中 n &= n - 1 用来清除最右边的 1

package main

import (
	"fmt"
)

func countBits(n uint) int {
	count := 0
	for n > 0 {
		n &= n - 1
		count++
	}
	return count
}

func main() {
	var n, m uint
	fmt.Scan(&n, &m)
	fmt.Println(countBits(n))
	fmt.Println(countBits(m))
}

中等题 宝石手串

  • 将原字符串 s 复制一份并拼接(s += s),这样可以处理环形的情况
  • 用哈希表 mp 记录每个字符最后一次出现的位置
  • 对于每个字符,查看它上一次出现的位置,计算中间需要移动的最小距离
  • 最终答案就是所有相同字符对之间最小的移动距离
package main

import "fmt"

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func solve(t int) {
	var (
		n int
		s string
	)
	fmt.Scan(&n, &s)
	s += s
	mp := make(map[rune]int)
	ans := n - 1
	for i, c := range s {
		if _, ok := mp[c]; ok {
			ans = min(ans, i-mp[c]-1)
		}
		mp[c] = i
	}
	if ans == n-1 {
		ans = -1
	}
	fmt.Println(ans)
}

func main() {
	var t int
	fmt.Scan(&t)
	for i := 0; i < t; i++ {
		solve(i)
	}
}

困难题 最长上升子序列(一)

方案一:线性DP 时间复杂度:

dp[i] 表示以第 i 个数结尾的最长递增子序列长度

  • 对于每个位置 i,初始化 dp[i] = 1(最短的递增子序列就是数字本身)
  • 遍历 i 前面的所有位置 j
  • 如果 a[i] > a[j],说明可以将第 i 个数接在以 j 结尾的递增子序列后面
  • 此时可以更新 dp[i] = max(dp[i], dp[j] + 1)
package main

import "fmt"

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func main() {
	var n int
	fmt.Scanf("%d", &n)
	a := make([]int, n)
	for i := range a {
		fmt.Scanf("%d", &a[i])
	}
	dp := make([]int, n)
	dp[0] = 1
	for i := 1; i < n; i++ {
		dp[i] = 1
		for j := 0; j < i; j++ {
			if a[i] > a[j] {
				dp[i] = max(dp[i], dp[j]+1)
			}
		}
	}
	ans := 0
	for _, v := range dp {
		ans = max(ans, v)
	}
	fmt.Println(ans)
}

方案二:贪心 + 二分查找 时间复杂度:

我们可以维护一个子序列 dp,其中 dp[i] 表示长度为 i+1 的上升子序列的最小结尾值,保证 dp 子序列一定是递增的

遍历所有的元素,如果当前元素比子序列中的最后一个元素还大,就将其添加到子序列的末尾

否则,我们可以用二分查找找到子序列中第一个大于等于当前元素的位置,将该位置上的元素替换为当前元素,从而保证子序列仍然是上升的。

最终,子序列的长度就是最长上升子序列的长度。

package main

import (
	"fmt"
	"sort"
)

func main() {
	var n int
	fmt.Scanf("%d", &n)
	a := make([]int, n)
	for i := range a {
		fmt.Scanf("%d", &a[i])
	}
	var dp []int
	for _, x := range a {
		idx := sort.SearchInts(dp, x)
		if idx == len(dp) {
			dp = append(dp, x)
		} else {
			dp[idx] = x
		}
	}
	fmt.Println(len(dp))
}

#牛客春招刷题训练营#
牛客春招刷题训练营 文章被收录于专栏

爱丽姐真是太好了

全部评论

相关推荐

03-16 16:25
门头沟学院 Java
艰难学习Java的鼠鼠:小林coding的解释: GET 和 POST 是 HTTP 协议中两种常用的请求方法,它们在不同的场景和目的下有不同的特点和用法。一般来说,可以从以下几个方面来区分二者(重点搞清两者在语义上的区别即可): 语义(主要区别):GET 通常用于获取或查询资源,而 POST 通常用于创建或修改资源。 幂等:GET 请求是幂等的,即多次重复执行不会改变资源的状态,而 POST 请求是不幂等的,即每次执行可能会产生不同的结果或影响资源的状态。 格式:GET 请求的参数通常放在 URL 中,形成查询字符串(querystring),而 POST 请求的参数通常放在请求体(body)中,可以有多种编码格式,如 application/x-www-form-urlencoded、multipart/form-data、application/json 等。GET 请求的 URL 长度受到浏览器和服务器的限制,而 POST 请求的 body 大小则没有明确的限制。不过,实际上 GET 请求也可以用 body 传输数据,只是并不推荐这样做,因为这样可能会导致一些兼容性或者语义上的问题。 缓存:由于 GET 请求是幂等的,它可以被浏览器或其他中间节点(如代理、网关)缓存起来,以提高性能和效率。而 POST 请求则不适合被缓存,因为它可能有副作用,每次执行可能需要实时的响应。 安全性:GET 请求和 POST 请求如果使用 HTTP 协议的话,那都不安全,因为 HTTP 协议本身是明文传输的,必须使用 HTTPS 协议来加密传输数据。另外,GET 请求相比 POST 请求更容易泄露敏感数据,因为 GET 请求的参数通常放在 URL 中。 再次提示,重点搞清两者在语义上的区别即可,实际使用过程中,也是通过语义来区分使用 GET 还是 POST。不过,也有一些项目所有的请求都用 POST,这个并不是固定的,项目组达成共识即可。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务