[Leetcode 1813] 句子相似性 III

给定两个字符串 sentence1 和 sentence2,每个表示由一些单词组成的一个句子。句子是一系列由 单个 空格分隔的 单词,且开头和结尾没有多余空格。每个单词都只包含大写和小写英文字母。
如果两个句子 s1 和 s2 ,可以通过往其中一个句子插入一个任意的句子(可以是空句子)而得到另一个句子,那么我们称这两个句子是 相似的 。注意,插入的句子必须与现有单词用空白隔开。
比方说,
  • s1 = "Hello Jane" 与 s2 = "Hello my name is Jane",我们可以往 s1 中 "Hello" 和 "Jane" 之间插入 "my name is" 得到 s2 。
  • s1 = "Frog cool" 与 s2 = "Frogs are cool" 不是相似的,因为尽管往 s1 中插入 "s are",它没有与 "Frog" 用空格隔开。
给你两个句子 sentence1 和 sentence2 ,如果 sentence1 和 sentence2 是 相似 的,返回 true ,否则返回 false 。
示例 1:
示例 2:
示例 3:
说明:
  • 1 <= sentence1.length, sentence2.length <= 100
  • sentence1 和 sentence2 都只包含大小写英文字母和空格。
  • sentence1 和 sentence2 中的单词都只由单个空格隔开。
 
 
这题两个句子相似,当且仅当较短句子的所有单词,都能被较长句子的前缀和后缀覆盖,中间多出来的一段可以看作被插入的内容。
比如:
这两个句子相似,因为:
前缀 "hello" 匹配,后缀 "jane" 匹配。较短句子 "hello jane" 的所有单词都被覆盖了,较长句子中间多出来的 "my name is" 就可以理解为插入进去的部分。
所以这题的核心是一个很具体的结构判断:
  • longer = prefix + inserted_middle + suffix
  • shorter = prefix + suffix
只要短句子的前缀部分和后缀部分能拼起来覆盖整个短句子,就返回 True

解法一:Deque —— 从两端不断弹出匹配单词

最直观的做法是把两个句子都拆成单词,然后放进双端队列。
我们先从前面开始比较。只要两个队列的队首单词相同,就同时弹出。
然后从后面开始比较。只要两个队列的队尾单词相同,也同时弹出。
如果最后其中一个队列被清空,说明较短的那个句子已经完全被前缀和后缀匹配掉了,中间剩下的部分就是另一个句子多出来的插入内容。
Deque 的写法非常贴合题意。
因为我们关心的就是:
  1. 开头能匹配多少;
  1. 结尾能匹配多少;
  1. 中间剩下的是否只属于其中一个句子。
如果某个句子的所有单词都被两端匹配消耗掉,那么另一个句子中间剩下的部分就可以作为插入句子。
Walk Through Example
从前面看,两个句子都以 "hello" 开头,所以弹掉 "hello"。 从后面看,两个句子都以 "jane" 结尾,所以弹掉 "jane"。 此时 sentence1 已经没有剩余单词,而 sentence2 还剩 "my name is"。 这正好表示可以往 sentence1 中间插入 "my name is",所以返回 True
复杂度分析
mn 分别是两个句子的字符串长度。
  • 时间复杂度: O(m + n)
    • 拆分句子需要线性时间,之后每个单词最多被弹出一次。
  • 空间复杂度: O(m + n)
    • 因为需要把两个句子的单词存入 deque。

解法二:双指针 —— 不弹出,只记录匹配边界 (推荐)

Deque 的做法很直观,但其实我们不需要真的弹出元素。
先把两个句子拆成单词数组,然后用双指针模拟从两端匹配:
  1. 从左往右匹配公共前缀;
  1. 从右往左匹配公共后缀。
最终要判断的是:较短句子是否能被前缀匹配和后缀匹配完全覆盖。
如果前缀匹配数量加上后缀匹配数量,覆盖了整个较短句子,那么返回 True
假设:
  • 较短句子是:shorter = A B C D
  • 较长句子是:longer = A B X Y Z C D
前缀匹配到 A B,后缀匹配到 C D。较短句子的所有单词都被覆盖了,中间的 X Y Z 就是插入部分。
复杂度分析
mn 分别是两个句子的字符串长度。
  • 时间复杂度: O(m + n)
    • 拆分字符串需要线性时间,双指针匹配过程中每个单词最多被访问一次。
  • 空间复杂度: O(m + n)
    • 因为 split() 会生成两个单词数组。
      如果把输入已经视为单词数组,则额外空间可以认为是 O(1)

总结

这题的关键是理解“插入任意句子”意味着什么。如果两个句子能通过插入变相等,那么:
  • 较长句子一定可以写成:prefix + inserted_middle + suffix
  • 较短句子一定是:prefix + suffix
所以我们只需要比较两端。先从开头匹配尽可能长的公共前缀,再从结尾匹配尽可能长的公共后缀。 如果较短句子的所有单词都被这两部分覆盖,就返回 True;否则返回 False
这两种方法本质完全一样。
  • Deque:真的从两端删除匹配单词。
  • Two Pointers:不删除,只用指针记录匹配到哪里。
Deque 写法更直观,特别适合第一次理解题意。
双指针写法更轻量。原因是它没有真实修改数据结构,只通过边界判断表达核心逻辑,代码也很短。
 
Buy Me a Coffee
上一篇
[Leetcode 1169] 无效交易
下一篇
Production Agents Need Workflow Graphs