1813. 句子相似性 III

判断两个句子是否相似 —— 插入句子判定问题解析

题目描述

给定两个字符串 sentence1sentence2,每个字符串表示一个句子,句子由若干单词组成,单词之间由单个空格分隔,且句子开头和结尾没有多余空格。每个单词只包含大小写英文字母。

如果存在一种方式,使得可以在其中一个句子中间插入一个句子(可能是空句子),并且通过插入使该句子变成另一个句子,则称这两个句子是 相似 的。

需要注意:

  • 插入的句子必须与已有单词之间用空格隔开。
  • 插入的句子可以为空,即不插入任何内容。
  • 插入的句子可以出现在句子中的任何位置(包括开头、中间、结尾)。

示例:

  • sentence1 = "Hello Jane"
    sentence2 = "Hello my name is Jane"
    可以往 sentence1 中的 "Hello""Jane" 之间插入 "my name is",因此两个句子相似。
  • sentence1 = "Frog cool"
    sentence2 = "Frogs are cool"
    不能通过插入一个空格分隔的句子使它们相似,因为不能将 "Frog""Frogs" 看作相同单词。

解题分析

题目的核心是判断两个句子是否能够通过在其中一个句子中插入一段连续的单词序列,使其变成另一个句子。

  • 这意味着两个句子中,除了中间插入的部分外,左右两边的单词顺序和内容必须保持不变且完全匹配。
  • 插入句子是连续的一段单词,可以为空。
  • 只需验证两句子是否有公共的前缀和公共的后缀覆盖较短句子的所有单词。

换句话说:

  • 找到两个句子从左边开始相同的最长单词序列(公共前缀)。
  • 找到两个句子从右边开始相同的最长单词序列(公共后缀)。
  • 如果这两个公共部分的长度加起来,至少覆盖了较短句子的长度,则说明短句子是长句子的一个“子序列”,中间差的部分就是插入的句子。

解题方法

  1. 拆分单词
    将两个句子用 split() 函数拆分成单词列表 words1words2
  2. 匹配公共前缀
    令指针 i 从左向右移动,比较 words1[i]words2[i],直到不相等或者到达任意句子的末尾。
  3. 匹配公共后缀
    令指针 j 从右向左移动,比较 words1[len(words1) - 1 - j]words2[len(words2) - 1 - j],直到不相等或者 i + j 达到较短句子的长度。
  4. 判断是否相似
    i + j >= min(len(words1), len(words2)),说明较短句子的所有单词均被前缀和后缀覆盖了,中间的部分即是插入句子,返回 True;否则返回 False

代码实现(Python)

class Solution:
    def areSentencesSimilar(self, sentence1: str, sentence2: str) -> bool:
        words1 = sentence1.split()
        words2 = sentence2.split()
        n1, n2 = len(words1), len(words2)

        i = 0
        # 找公共前缀
        while i < n1 and i < n2 and words1[i] == words2[i]:
            i += 1

        j = 0
        # 找公共后缀
        while j < n1 - i and j < n2 - i and words1[n1 - 1 - j] == words2[n2 - 1 - j]:
            j += 1

        return i + j >= min(n1, n2)

代码解析

  • words1words2 是两个句子的单词列表。
  • 变量 i 用来计算最大公共前缀长度。
  • 变量 j 用来计算最大公共后缀长度,注意它的遍历范围是从句尾开始,且不与前缀部分重叠。
  • 最后判断是否覆盖了较短句子所有单词(i + j >= min(n1, n2))。

时间复杂度与空间复杂度

  • 时间复杂度:O(N),其中 N 是两个句子中较短句子的单词数。因为只需要一次从左向右扫描和一次从右向左扫描。
  • 空间复杂度:O(N),拆分单词时产生的列表空间。

示例说明

sol = Solution()

print(sol.areSentencesSimilar("Hello Jane", "Hello my name is Jane"))
# 输出: True
# 解释: "my name is" 可以插入到 "Hello" 和 "Jane" 之间。

print(sol.areSentencesSimilar("Frog cool", "Frogs are cool"))
# 输出: False
# 解释: 前缀单词不匹配,不能插入满足条件的句子。

print(sol.areSentencesSimilar("I love coding", "I love coding"))
# 输出: True
# 解释: 两个句子完全相同,插入空句子即可。

print(sol.areSentencesSimilar("Today is", "Today is a good day"))
# 输出: True
# 解释: 插入 "a good day" 到 "Today is" 的末尾。

总结

通过匹配两个句子的最长公共前缀和后缀,判断是否可以通过插入一段句子使其相等,是解决本题的关键。代码实现简洁明了,效率高,且易于理解。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值