- 套路题:异或找独特数字
- 一维dp:找出最长的神奇数列
- 贪心:字典序最小的01字符串
1. 套路题:异或找独特数字
问题描述
在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。
要求:
- 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。
- 尽量减少额外空间的使用,以体现你的算法优化能力。
测试样例
样例1:
输入:
cards = [1, 1, 2, 2, 3, 3, 4, 5, 5]
输出:4
套路
遍历一遍所有数,用一个变量记录所有值的异或结果即可。
def solution(inp)-> int :
res=0
for i in inp: res=res^i
return res
AI练中学评价
测试Prompt:默认的思路提示
部分结果:
解题思路
异或运算的特性:
- 异或运算有一个非常有用的特性:
a ^ a = 0和a ^ 0 = a。这意味着,如果我们将数组中所有数字进行异或运算,出现两次的数字会相互抵消,最终剩下的就是唯一出现一次的数字。
个人评价:效果很好,答出异或就行。
2. 一维dp:找出最长的神奇数列
问题描述
小F是一个好学的中学生,今天他学习了数列的概念。他在纸上写下了一个由 0 和 1 组成的正整数序列,长度为 n。这个序列中的 1 和 0 交替出现,且至少由 3 个连续的 0 和 1 组成的部分数列称为「神奇数列」。例如,10101 是一个神奇数列,而 1011 不是。现在,小F想知道在这个序列中,最长的「神奇数列」是哪一个。你能帮他找到吗?
如果有多个神奇数列,那么输出最先出现的一个。
测试样例
样例1:
输入:
inp = "0101011101"
输出:'010101'
思路
状态转移方程:
对于任意一位数字(索引i),我们可以从这个角度考虑:神奇数列是不是从i这里开始(是不是神奇数列的首元素),只有2种值:是或不是(接着前面的数列)。基于此,我们可以拆解出sequence的每一位元素与剩余sequence的关系,列出上面的状态转移方程,从而解决问题。
实现上,我们遍历一次输入的字符串的每一位数,假定在循环中,每个epoch的索引对应数都是会被选中的,那么要么它是神奇数列的首元素,要么它接着前面的元素。
这种一维的动态规划问题是比较基础的dp,在遍历过程中用一个变量记录需要的最大值即可。
def solution(inp:str)->str:
if not inp or len(inp)<3: return ""
seq=inp[0]
maxs=seq
for i in range(1,len(inp)):
# 01元素交替出现,那么该元素i接在前面
if inp[i] != inp[i-1]:
seq+=inp[i]
if len(seq)>len(maxs):
maxs=seq
# 否则,神奇数列从i 这里重新开始
else:
seq=inp[i]
return maxs if len(maxs)>=3 else ""
AI练中学评价
- 测试Prompt:
请你从动态规划的角度,考虑给出思路提示
- 部分结果:
好的,让我们从动态规划的角度来考虑这个问题。
问题理解 我们需要在一个由 0 和 1 组成的字符串中找到最长的「神奇数列」。神奇数列的定义是:
由 0 和 1 交替出现。 长度至少为 3。 动态规划思路 动态规划的核心思想是将问题分解为子问题,并通过存储子问题的解来避免重复计算。
状态定义 我们可以定义一个二维数组 dp,其中 dp[i][0] 表示以第 i 个字符结尾的最长神奇数列的长度,且该数列以 0 结尾;dp[i][1] 表示以第 i 个字符结尾的最长神奇数列的长度,且该数列以 1 结尾。
状态转移方程 如果 inp[i] == '0': dp[i][0] = dp[i-1][1] + 1,因为当前字符是 0,所以它只能接在以 1 结尾的数列后面。 dp[i][1] = 0,因为当前字符是 0,不能接在以 1 结尾的数列后面。 如果 inp[i] == '1': dp[i][1] = dp[i-1][0] + 1,因为当前字符是 1,所以它只能接在以 0 结尾的数列后面。 dp[i][0] = 0,因为当前字符是 1,不能接在以 0 结尾的数列后面。 初始条件 dp[0][0] = 1 如果 inp[0] == '0',否则 dp[0][0] = 0。 dp[0][1] = 1 如果 inp[0] == '1',否则 dp[0][1] = 0。
- 个人评价:效果不太行。似乎它会把dp默认为需要用二维数组的形式,难道是训练语料里背包问题之类的太多了?
3. 贪心:字典序最小的01字符串
问题描述
小U拥有一个由0和1组成的字符串,她可以进行最多k次操作,每次操作可以交换相邻的两个字符。目标是通过这些操作,使得最终得到的字符串字典序最小。
例如,小U当前有一个字符串 01010,她最多可以进行 2 次相邻字符交换操作。通过这些操作,她可以将字符串调整为 00101,这是可以通过不超过2次操作得到的字典序最小的字符串。
现在,小U想知道,经过最多k次操作后,能够得到的字典序最小的字符串是什么。
测试样例
样例1:
输入:
n = 5, k = 2, s = "01010"
输出:'00101'
思路
因为没有时间限制,所以我感觉暴力做就行。 k次操作就是一个k轮的for 循环,每轮的操作就是先定位,然后再交换。 定位:找到字符串最左边的、右边有0的那个1 交换:将这个1和右边的0交换
def locate(s:str) -> int:
for i in range(len(s)):
if s[i]=='1' and i+1<len(s) and s[i+1]=='0':
break
return i
def solution(n: int, k: int, s: str) -> str:
res=s
for epoch in range(k):
i= locate(res)
if i<len(s)-1:
res=res[:i]+"01"+(res[i+2:] if i+2 <len(res) else "")
else: break
return res
AI练中学
-
测试Prompt: 默认的选项,给出思路提示
-
部分结果:
要解决这个问题,我们需要通过最多
k次相邻字符交换操作,使得字符串的字典序最小。以下是解题思路:解题思路
理解字典序最小:
- 字典序最小意味着我们需要尽可能地将
0放在前面,1放在后面。贪心策略:
- 我们可以从左到右遍历字符串,尝试将当前位置的
0尽可能地向左移动。- 每次移动一个
0到当前位置,需要计算移动的步数,并确保不超过k次操作。具体步骤:
初始化一个空的结果字符串。
遍历原始字符串,对于每个字符:
- 如果当前字符是
0,尝试将其移动到结果字符串的当前位置。- 计算移动所需的步数,并更新剩余的操作次数
k。如果当前字符是
1,直接将其添加到结果字符串中。边界情况:
- 如果
k为 0,则不能再进行任何操作,直接返回当前结果字符串。- 如果字符串中没有
0,则直接返回原始字符串。
- 个人评价:不错,我觉得能答出贪心和边界情况还可以了