058 · Merge Strings Alternately
My First Thoughts
嗯,刻意要求的一道更容易一点点的题目。
比如:
word1 = "abc"
word2 = "pqr"这道题需要拼接两个字符串,对吧。
之前应该是遇到过合并两个升序链表的题。这里可能更简单一些,因为不用判断大小,只是按顺序交替拿字符。
应该是维护指针就行了吧?
其实只有一个问题:
如何实现交替,如何退出?
一开始可能会想到维护一个总的 index。
如果 index 是偶数,就取 word1。
如果 index 是奇数,就取 word2。
比如:
index = 0 取 word1[0]
index = 1 取 word2[0]
index = 2 取 word1[1]
index = 3 取 word2[1]
这样就会想到类似:
append(index // 2)
index += 1不过这里马上会有一点麻烦:当前要取的那个字符串是不是已经用完了。
所以会继续想:
两个指针会好些嘛?
也就是:
result = []
index1 = 0
index2 = 0用 index1 表示 word1 取到哪里,用 index2 表示 word2 取到哪里。
然后尝试根据两个指针的大小决定下一步拿哪个字符串:
if index1 <= index2 and index1 < len(word1):
result.append(word1[index1])
index1 += 1
elif index1 > index2 and index2 < len(word2):
result.append(word2[index2])
index2 += 1
elif index1 > len(word1) and index2 <= len(word2):
left = word2[index2:]
return "".join(result) + left
else:
left = word1[index1:]
return "".join(result) + left这个方向是可以的。
真正重要的点已经抓到了:两个字符串各自需要一个位置,最后还要处理剩余部分。
不过这个写法里,边界和分支会有点绕。
Why That Is Not Enough
上面的两个指针思路本身没有错。
问题主要是表达方式有点复杂。
它用:
index1 <= index2
index1 > index2来判断现在应该取 word1 还是 word2。
这样能做,但需要小心维护两个指针之间的关系。再加上两个字符串长度可能不同,后面就会出现比较多的边界分支。
这题其实可以更直接一点。
因为题目要求的是交替合并,而交替合并可以理解成:
每一轮尽量取一对字符:先取
word1当前字符,再取word2当前字符。
也就是说,不需要一次只取一个字符,也不需要用指针大小判断轮到谁。
只要两个字符串都还有字符,就一轮取两个。
Final Idea
还是保留两个指针:
i = 0
j = 0其中:
i指向word1当前要取的位置j指向word2当前要取的位置
先准备一个结果列表:
result = []当两个字符串都还没有用完时:
while i < len(word1) and j < len(word2):每一轮就取一对字符:
result.append(word1[i])
result.append(word2[j])
i += 1
j += 1这样天然就是交替的:
word1[i], word2[j]
word1[i + 1], word2[j + 1]
word1[i + 2], word2[j + 2]
循环结束以后,说明至少有一个字符串已经用完了。
如果 word1 还有剩余,就把剩下的部分接上:
result.append(word1[i:])如果 word2 还有剩余,也把剩下的部分接上:
result.append(word2[j:])这里不用再额外判断谁剩下。
因为如果某个字符串没有剩余,切片会得到空字符串:
word1[i:] == ""把空字符串 append 进去也不影响最后结果。
Why It Works
题目要求从 word1 开始交替取字符。
当 word1 和 word2 都还有字符时,每一轮执行:
result.append(word1[i])
result.append(word2[j])这正好对应:
先取 word1 的当前字符
再取 word2 的当前字符
然后两个指针同时往后移动一格:
i += 1
j += 1所以下一轮会继续取下一对字符。
当循环结束时,至少有一个字符串已经没有字符可以取了。
这时候交替已经无法继续,只需要按照题意,把另一个字符串剩下的字符直接接到结果后面。
用切片:
word1[i:]
word2[j:]正好表示两个字符串还没有取过的部分。
如果其中一个已经用完,它的剩余部分就是空字符串,不会改变答案。
例如:
word1 = "ab"
word2 = "pqrs"先取两轮:
a p b q
这时 word1 用完了,word2[j:] 是 "rs"。
所以最后得到:
"apbqrs"
Code
def mergeAlternately(word1, word2):
result = []
i = 0
j = 0
while i < len(word1) and j < len(word2):
result.append(word1[i])
result.append(word2[j])
i += 1
j += 1
result.append(word1[i:])
result.append(word2[j:])
return "".join(result)Complexity
| Time | \(O(n + m)\) - n 是 word1 的长度,m 是 word2 的长度;每个字符都会被加入结果一次 |
| Space | \(O(n + m)\) - result 需要保存合并后的字符串内容 |
Takeaway
如果题目要求“交替处理两个序列”,不一定要用一个总下标和奇偶判断。也可以用两个指针,每一轮同时处理一对元素;等其中一个序列用完后,再把另一个序列的剩余部分接上。
← Quiz