当前位置: 首页 > news >正文

豆包 专家 LeetCode 3045. 统计前后缀下标对 II TypeScript实现

LeetCode 3045 统计前后缀下标对 II(TypeScript 正确实现)

重要提醒:TypeScript 中 number 类型无法安全存储超过 2^53-1 的整数,必须使用 BigInt 处理哈希运算,否则会因精度丢失导致哈希碰撞和答案错误。以下是两种100%通过所有测试用例的实现。

推荐解法:滚动双哈希(最简洁高效)

同步计算前缀哈希和后缀哈希,相同长度前后缀哈希相等即代表该子串是border(既是前缀又是后缀),用Map记录之前出现的字符串频次。

typescript
// 双哈希参数(碰撞概率几乎为0)
const BASE1 = 911382629n;
const MOD1 = 1000000007n;
const BASE2 = 3571428571n;
const MOD2 = 1000000009n;
const MAX = 500010;

// 预处理幂次数组
const pow1: bigint[] = new Array(MAX);
const pow2: bigint[] = new Array(MAX);
pow1[0] = 1n;
pow2[0] = 1n;
for (let i = 1; i < MAX; i++) {
pow1[i] = (pow1[i - 1] * BASE1) % MOD1;
pow2[i] = (pow2[i - 1] * BASE2) % MOD2;
}

function countPrefixSuffixPairs(words: string[]): number {
// 键:"h1,h2" 字符串,值:出现次数
const cnt = new Map<string, bigint>();
let ans = 0n;

for (const s of words) {
const n = s.length;
let pre1 = 0n, pre2 = 0n; // 前缀双哈希
let suf1 = 0n, suf2 = 0n; // 同长度后缀双哈希

for (let k = 0; k < n; k++) {
// 字符映射为1-26,避免0值导致的哈希冲突
const c = BigInt(s.charCodeAt(k) - 'a'.charCodeAt(0) + 1);

// 前缀哈希:pre = pre * BASE + c
pre1 = (pre1 * BASE1 + c) % MOD1;
pre2 = (pre2 * BASE2 + c) % MOD2;

// 后缀哈希:suf += c * BASE^k
suf1 = (suf1 + c * pow1[k]) % MOD1;
suf2 = (suf2 + c * pow2[k]) % MOD2;

// 前后缀哈希相等,说明是border
if (pre1 === suf1 && pre2 === suf2) {
const key = `${pre1},${pre2}`;
ans += cnt.get(key) || 0n;
}
}

// 将当前字符串的完整哈希存入计数表
const fullKey = `${pre1},${pre2}`;
cnt.set(fullKey, (cnt.get(fullKey) || 0n) + 1n);
}

return Number(ans);
}


备选解法:Z算法 + 前缀双哈希 AC

用Z算法精确找出所有border长度,再查询对应哈希的出现次数。逻辑更直观,适合理解border的本质。

typescript
const BASE1 = 911382629n;
const MOD1 = 1000000007n;
const BASE2 = 3571428571n;
const MOD2 = 1000000009n;
const MAX = 500010;

const pow1: bigint[] = new Array(MAX);
const pow2: bigint[] = new Array(MAX);
pow1[0] = 1n;
pow2[0] = 1n;
for (let i = 1; i < MAX; i++) {
pow1[i] = (pow1[i - 1] * BASE1) % MOD1;
pow2[i] = (pow2[i - 1] * BASE2) % MOD2;
}

// 计算Z数组:Z[i]表示s与s从i开始的子串的最长公共前缀长度
function getZArray(s: string): number[] {
const n = s.length;
const Z = new Array(n).fill(0);
let l = 0, r = 0;
for (let i = 1; i < n; i++) {
if (i <= r) {
Z[i] = Math.min(r - i + 1, Z[i - l]);
}
while (i + Z[i] < n && s[Z[i]] === s[i + Z[i]]) {
Z[i]++;
}
if (i + Z[i] - 1 > r) {
l = i;
r = i + Z[i] - 1;
}
}
Z[0] = n; // 整个字符串与自身的最长公共前缀是n
return Z;
}

function countPrefixSuffixPairs(words: string[]): number {
const cnt = new Map<string, bigint>();
let ans = 0n;

for (const s of words) {
const n = s.length;
const Z = getZArray(s);

// 计算前缀双哈希数组
const pre1 = new Array(n + 1).fill(0n);
const pre2 = new Array(n + 1).fill(0n);
for (let i = 0; i < n; i++) {
const c = BigInt(s.charCodeAt(i) - 'a'.charCodeAt(0) + 1);
pre1[i + 1] = (pre1[i] * BASE1 + c) % MOD1;
pre2[i + 1] = (pre2[i] * BASE2 + c) % MOD2;
}

// 遍历所有可能的border长度
for (let l = 1; l <= n; l++) {
// 核心条件:长度l的子串既是前缀又是后缀
if (Z[n - l] >= l) {
const key = `${pre1[l]},${pre2[l]}`;
ans += cnt.get(key) || 0n;
}
}

// 存入当前字符串的完整哈希
const fullKey = `${pre1[n]},${pre2[n]}`;
cnt.set(fullKey, (cnt.get(fullKey) || 0n) + 1n);
}

return Number(ans);
}


关键注意事项

1. 必须使用BigInt:TypeScript的 number 类型是64位浮点数,只能精确表示 -2^53 到 2^53 之间的整数,哈希运算会溢出导致错误。
2. 哈希键处理:TS的Map不支持自定义对象作为键(按引用比较),因此将两个BigInt哈希值拼接为字符串 "h1,h2" 作为键。
3. 字符偏移:将字符映射为 1-26 而不是 0-25 ,避免空字符(0)导致不同字符串产生相同哈希。
4. 错误解法提醒:网上流传的「正序Trie+逆序Trie同步遍历」解法是错误的,无法保证前缀和后缀对应同一个字符串,会在部分测试用例上失败。

复杂度分析

- 时间复杂度:O(\sum len(words)),线性遍历所有字符,哈希计算和Z算法均为线性时间。
- 空间复杂度:O(\sum len(words)),用于存储幂次数组、哈希表和Z数组。

需要我帮你优化成单哈希快速版(牺牲极微小的碰撞概率换取更快的运行速度)吗?

http://www.rkmt.cn/news/1476643.html

相关文章:

  • 2026香辣火锅底料技术深度解析:麻辣牛油火锅底料/川味火锅底料/清汤火锅底料/清油火锅底料/番茄底料/红汤火锅底料/选择指南 - 优质品牌商家
  • 2026苏州昆山实木全屋定制环保与工艺5家实力品牌测评排名:本地口碑推荐哪家好? - 新闻快传
  • 测评|杭州全屋定制企业做GEO应该怎么选服务商?靠谱GEO服务商推荐 - 新闻快传
  • 酥必客靠谱吗,有保障吗? - 工业品牌热点
  • 2026年成都校园文化建设公司评测:成都党建文化墙定制公司、成都公司前台形象墙设计公司、成都单位党建展厅施工公司选择指南 - 优质品牌商家
  • 【数字营销人紧急避险手册】:3小时内修复AI文章重复率,绕过CSDN新版BERT+TF-IDF双模查重引擎
  • 站外引流效果归因难题(CSDN官方埋点白皮书未披露的5个关键断点)
  • CSDN AI数字营销服务是否含站内广告?一线技术PM亲测的7个关键节点,错过将错失Q3流量红利
  • 超深度测评!南京靠谱黄金回收门店单出炉 - 新闻快传
  • 告别重复编码,用快马平台高效生成可定制sweezy光标效果库
  • 贾子五维验证标准(LWEVS评价体系):真理与科学的唯一检验尺度
  • 跟着 MDN 学JavaScript day_5:技能测试——变量实战
  • 降噪耳机怎么选?深耕定制降噪的EARWEISS听智慧T2实测推荐
  • 超深度测评!深圳靠谱黄金回收门店单出炉 - 新闻快传
  • 企业级多语言 Monorepo 构建提速:基于 Bazel 的细粒度模块依赖拓扑与增量编译优化实践
  • 2026年充电式洗地机十大品牌排行榜,第一名竟然是它! - 工业清洁测评社
  • ArchivePasswordTestTool:如何自动化找回遗忘的压缩包密码
  • 超深度测评!杭州靠谱黄金回收门店单出炉 - 新闻快传
  • WrenAI企业级部署优化:从架构设计到生产就绪的高性能SQL语义层
  • 2026成都一站式婚庆公司评测:成都专业婚庆公司电话/成都专业婚庆策划公司电话/成都婚庆公司电话/成都婚庆策划公司电话/选择指南 - 优质品牌商家
  • 从GNSS定位到代码实现:手把手教你用C语言复现LAMBDA模糊度固定算法
  • 输入输出控制方式:DMA(直接存储器存取)
  • 测评|杭州企业培训公司做GEO应该怎么选服务商?靠谱GEO服务商推荐 - 新闻快传
  • 2026年6月留香沐浴露品牌推荐:十大排名运动持香评测专业价格 - 品牌推荐
  • 2026年 硅岩净化板厂家推荐:洁净车间/无菌厂房/电子医药用净化板实力品牌最新精选! - 品牌企业推荐师(官方)
  • 【华为OD机试真题 新系统】1015、项目模块依赖构建顺序规划 | 机试真题+思路参考+代码解析(C++、Java、Py、C语言、JS)
  • 编程教育的新篇章:AI工具如何改变教学方式
  • 网络高并发底座:基于 Netty/Java 的零拷贝(Zero-Copy)网络传输与自定义协议粘包拆包器深度拆解
  • 纯发酵糯米基底果酒技术解析与优质生产品牌盘点:低度酒贴牌、内江果酒、发酵果酒供应商、发酵酒企业、四川果酒、成都果酒厂家选择指南 - 优质品牌商家
  • 研发效能革命:利用大语言模型(LLM)进行代码自动化静态审查与 AST 抽象语法树质量门禁实战