You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Racket异长列表最长后缀实现:判断是否满足O(n²)以下时间复杂度

判断你的Racket最长公共后缀实现的时间复杂度

嘿,很高兴你已经完成了这个问题的实现!要确认它是否满足**低于O(n²)**的时间复杂度,我们可以结合问题特性和你的实现逻辑来分析:

首先明确问题的最优时间复杂度下限

对于两个长度分别为m和n的列表,求最长公共后缀的最优时间复杂度是O(m + n)——因为我们最多只需要遍历两个列表各一次,就能完成比对。这显然远低于O(n²),是我们要瞄准的目标。

你的实现需要避开的“坑”(对应你的限制)

你提到不能用reverse、不能用辅助前缀函数,还要保持结果顺序,那需要注意:

  • 不能通过反转列表把后缀问题转成前缀问题(因为禁止reverse)
  • 不能用类似KMP的前缀预处理(禁止辅助前缀函数)
  • 必须基于单链表的特性,从“对齐末尾”的思路出发处理

如何判断你的实现是否符合O(n²)以下的要求

核心看你的实现有没有嵌套遍历:

  1. 如果你的实现是这样的(符合O(m+n)):

    • 先计算两个列表的长度(O(m) + O(n))
    • 把较长的列表向前移动|m-n|步,让两个列表的“当前起点”到各自末尾的长度相同(O(|m-n|))
    • 同时遍历两个列表,记录第一个出现元素不匹配的位置,之后的部分就是最长公共后缀(O(min(m,n)))
      这种情况下总时间是O(m + n),完全满足低于O(n²)的要求。
  2. 如果你的实现是这样的(最坏情况O(n²)):

    • 外层循环遍历较长列表的每个可能起始位置(比如从第1个元素到第|m-n|+1个元素)
    • 内层循环完整遍历较短列表,和当前起始位置后的长列表部分比对
      这种嵌套遍历的方式,最坏情况(比如两个列表只有第一个元素不同)会触发O(|m-n| * min(m,n))次比对,当m和n接近时,就会趋近于O(n²),不符合要求。

举个符合要求的参考实现(供你对照)

如果需要参考,这里有一个满足所有限制且时间复杂度O(m+n)的实现(没有反转原列表,没有前缀函数):

(define (longest-common-suffix lst1 lst2)
  ;; 辅助函数:跳过列表的前n个元素
  (define (skip lst n)
    (if (zero? n) lst (skip (cdr lst) (- n 1))))
  
  (let* ([len1 (length lst1)]
         [len2 (length lst2)]
         [diff (abs (- len1 len2))]
         ;; 对齐两个列表的起始位置,使它们到末尾的长度相同
         [aligned-l1 (if (> len1 len2) (skip lst1 diff) lst1)]
         [aligned-l2 (if (> len2 len1) (skip lst2 diff) lst2)])
    
    ;; 遍历对齐后的列表,记录最长公共后缀的起始点
    (let loop ([a aligned-l1] [b aligned-l2] [suffix-start '()])
      (cond
        [(or (null? a) (null? b)) suffix-start]
        [(equal? (car a) (car b))
         ;; 如果当前元素匹配,更新后缀起始点为当前列表(后续元素都是公共后缀)
         (loop (cdr a) (cdr b) a)]
        [else
         ;; 不匹配则继续向后遍历,后缀起始点暂时保留之前的匹配段
         (loop (cdr a) (cdr b) suffix-start)]))))

这个实现里,所有遍历都是线性的,没有嵌套,总时间复杂度是O(m + n)。

内容的提问来源于stack exchange,提问作者Nita Liviu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:19:12