题目:
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,
"ace"
是"abcde"
的一个子序列,而"aec"
不是)。进阶:
如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
示例 1:
输入:s = "abc", t = "ahbgdc" 输出:true示例 2:
输入:s = "axc", t = "ahbgdc" 输出:falsef
方法1:双指针法
思路(以图解的形式):
文字解释:
在这个方法中,我们使用两个指针
i
和j
分别遍历字符串s
和t
。我们逐个字符比较s[i]
和t[j]
:
- 如果
s[i] == t[j]
,说明我们在t
中找到了s
的当前字符,因此i
向前移动一位。- 无论
s[i]
和t[j]
是否相等,j
都会向前移动一位,因为我们总是在t
中向前查找。当循环结束时,如果
i
等于s
的长度,这意味着我们成功找到了s
中的所有字符在t
中的对应位置,因此s
是t
的子序列。否则,s
不是t
的子序列。
代码实现:
class Solution: def isSubsequence(self, s: str, t: str) -> bool: n,m = len(s),len(t) i=j=0 while i<n and j<m: if s[i] == t[j]: i += 1 j+=1 return i==n
方法2:动态规划
思路:
图解:
文字解释:
使用动态规划来预处理字符串
t
。我们创建一个二维数组dp
,其中dp[i][j]
表示在t
中从位置i
开始,字符'a' + j
的下一个位置。我们初始化dp
的所有值为0,然后从t
的最后一个字符开始向前填充dp
。对于
t
中的每个字符t[i]
,我们首先复制下一个位置的值,然后更新t[i]
对应的字符位置。这样,dp[i][j]
就会存储从位置i
开始在t
中字符'a' + j
的下一个位置。在预处理完成后,我们遍历
s
中的每个字符。对于每个字符s[i]
,我们查找它在t
中的下一个位置。如果这个位置是0,说明在t
中不存在这个字符,因此s
不是t
的子序列。如果找到了位置,我们就更新index
到这个位置,继续检查下一个字符。代码的实现:
class Solution: def isSubsequence(self, s: str, t: str) -> bool: dp = [[0] * 26 for _ in range(len(t) + 1)] for i in range(len(t) - 1, -1, -1): for j in range(26): dp[i][j] = dp[i + 1][j] dp[i][ord(t[i]) - ord('a')] = i + 1 index = 0 for char in s: if dp[index][ord(char) - ord('a')] == 0: return False index = dp[index][ord(char) - ord('a')] return True
方法3:二分查找
思路:
我们首先为
t
中的每个唯一字符创建一个索引列表。然后,我们遍历s
中的每个字符,并使用二分查找在t
的对应字符索引列表中查找大于上一个找到索引的位置。
binary_search
函数接受一个字符的索引列表和上一个找到的索引target
。我们在这个列表中使用二分查找来找到大于target
的最小索引。如果找到了这样的索引,我们就更新prev_index
并继续检查下一个字符。如果没有找到,说明s
不是t
的子序列。代码的实现:
class Solution: def isSubsequence(self, s: str, t: str) -> bool: indexes = {char: [] for char in set(t)} for index, char in enumerate(t): indexes[char].append(index) prev_index = -1 for char in s: if char not in indexes: return False char_indexes = indexes[char] index = self.binary_search(char_indexes, prev_index) if index == -1: return False prev_index = index return True def binary_search(self, indexes, target): left, right = 0, len(indexes) - 1 while left <= right: mid = (left + right) // 2 if indexes[mid] > target: right = mid - 1 elif indexes[mid] < target: left = mid + 1 else: return mid return left if indexes[left] > target else -1
关于进阶问题的回答:
如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,可以使用动态规划或二分查找法来预处理t。