好的,我们来解答LeetCode上的问题392:“判断子序列”。
一、问题描述
给定字符串 s 和 t,判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。 进阶: 如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
二、方案一:双指针法
1、思路
我们可以使用双指针的方法来解决这个问题。一个指针指向字符串 s,另一个指针指向字符串 t。我们遍历字符串 t,如果当前字符与 s 的指针指向的字符相同,则 s 的指针向前移动。如果 s 的指针能够遍历完整个 s,则说明 s 是 t 的子序列。
2、代码实现
go
func isSubsequence(s string, t string) bool {
i, j := 0, 0
for i < len(s) && j < len(t) {
if s[i] == t[j] {
i++
}
j++
}
return i == len(s)
}
3、复杂度分析
- 时间复杂度:O(n),其中 n 是字符串 t 的长度。我们只需要遍历一次字符串 t。
- 空间复杂度:O(1),我们只需要常数级别的额外空间。
三、方案二:动态规划
1、思路
对于进阶问题,如果有大量输入的 S,我们可以使用动态规划的方法来优化。我们可以预处理字符串 t,创建一个二维数组 dp,其中 dp[i][j] 表示字符串 t 中从位置 i 开始,长度为 j 的子串中,字符 t[j] 的位置。这样,对于每个输入的 S,我们可以快速地判断它是否为 T 的子序列。
2、代码实现
go
func isSubsequence(s string, t string) bool {
if s == "" {
return true
}
m, n := len(s), len(t)
dp := make([][]bool, m+1)
for i := 0; i <= m; i++ {
dp[i] = make([]bool, n+1)
}
dp[0][0] = true
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
if s[i-1] == t[j-1] {
dp[i][j] = dp[i-1][j-1] || i == 1
} else {
dp[i][j] = dp[i][j-1]
}
}
}
return dp[m][n]
}
3、复杂度分析
- 时间复杂度:O(m),其中 m 是字符串 s 的长度。我们只需要遍历一次字符串 s。
- 空间复杂度:O(1),我们只需要常数级别的额外空间。
四、总结
对于一般情况,双指针法是一个简单且高效的方法。对于有大量输入的情况,动态规划方法可以进一步优化时间复杂度。