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

区间多字符串两两最长公共前缀查询的高效数据结构求解问询

首先得明确你问题里的“区间内所有字符串两两之间的最长公共前缀”具体指哪种场景——两种常见理解对应完全不同的解法,我都会帮你梳理清楚:

场景1:求区间内所有字符串共有的最长公共前缀

(即所有字符串都包含的最长前缀,比如区间内是["apple", "app", "application"],结果是"app")

这种场景用线段树就能高效解决,完全符合你的需求:

预处理步骤

  1. 前缀哈希预处理:
    • 给每个字符串计算前缀哈希数组(推荐用双哈希降低冲突概率),这样可以O(1)比较任意两个字符串的前k个字符是否相同。
    • 例如,对于字符串s,哈希数组h[i]表示s[0..i-1]的哈希值,pow_base[i]表示基数的i次幂,这样两个字符串s1和s2的前k个字符相等等价于h1[k] * pow_base[len(s2)] == h2[k] * pow_base[len(s1)](具体公式根据你选的哈希方式调整)。
  2. 构建线段树:
    • 线段树的每个节点对应原数组的一个区间[L, R],每个节点存储两个信息:
      • lcp_len:该区间内所有字符串共有的最长公共前缀长度。
      • sample:该区间的一个代表字符串(比如取区间第一个字符串s[L]),用于和其他区间的代表字符串比较前缀。
    • 线段树的合并逻辑:
      对于左右子节点left和right:
      • 先取初始候选长度candidate = min(left.lcp_len, right.lcp_len)。
      • 用哈希比较left.sample和right.sample的前candidate个字符是否相同:
        • 如果相同,合并后的lcp_len就是candidate;
        • 如果不同,通过二分查找找到最大的k < candidate,使得两个字符串的前k个字符相同,这个k就是合并后的lcp_len。
    • 线段树构建的时间复杂度是O(n log n * log L),其中L是字符串的最大长度,结合总字符串长度≤1e5的条件,完全可行。

查询操作

对于任意查询[l, r],将区间拆分为线段树中的若干节点,依次合并这些节点的lcp_len和sample信息,最终得到的lcp_len就是答案。单次查询时间复杂度是O(log n * log L)。


场景2:求区间内任意两两字符串的LCP的最大值

(即找到区间内一对字符串,它们的LCP是所有两两对中最大的,比如区间内是["apple", "app", "banana"],结果是3,来自"apple"和"app"的LCP)

这种场景需要结合后缀数组、RMQ和离线处理,复杂度可以控制在O((n+q) log n):

预处理步骤

  1. 构建后缀数组与相关结构:
    • 将所有字符串拼接成一个大字符串,每个字符串之间用一个不在原字符集中的分隔符(比如#)隔开,确保不同字符串的后缀不会产生虚假的公共前缀。
    • 对这个大字符串构建后缀数组SA、排名数组rank(rank[i]表示原数组第i个字符串对应的后缀在SA中的排名),以及height数组(height[i]表示SA[i]和SA[i-1]对应的后缀的LCP长度)。
    • 对height数组构建ST表,支持O(1)查询任意区间的最小值(这个最小值就是对应两个后缀的LCP长度)。
  2. 离线预处理查询与边权:
    • 我们知道,两两字符串的最大LCP一定出现在字典序相邻的字符串对中(证明:对于三个字典序排序后的字符串s1 < s2 < s3,LCP(s1,s3) = min(LCP(s1,s2), LCP(s2,s3)),不可能大于其中任意一个)。
    • 因此,我们只需要关注字典序相邻的字符串对,计算它们的LCP值(通过ST表查询height区间的最小值),并将这些对转化为(a, b, val),其中a和b是这对字符串在原数组中的位置(确保a < b),val是它们的LCP值。

离线查询处理(线段树+排序)

  1. 排序准备:
    • 将所有查询按右端点r从小到大排序,每个查询记录l, r, 查询索引(用于最后输出答案)。
    • 将所有(a, b, val)按b从小到大排序。
  2. 线段树维护最大值:
    • 初始化一个支持单点更新和区间最大值查询的线段树,初始值全为0。
    • 用两个指针分别遍历查询和点对:
      • 对于当前处理的查询(右端点为r),先将所有b ≤ r的点对加入线段树:更新线段树中位置a的值为max(当前值, val)(因为这对字符串的两个位置都≤r,只要a ≥ l就属于查询区间)。
      • 查询线段树的区间[l, r]的最大值,这个值就是该查询的答案(如果区间长度为1,答案为0)。

内容的提问来源于stack exchange,提问作者Dean Ambrose

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:18:08