当前位置: 首页 > 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.jsqmd.com/news/964915/

相关文章:

  • 2026年氟碳铝单板厂家推荐:氟碳铝单板幕墙/造型天花/车间厂房铝单板品牌实力与经典案例深度解析 - 品牌企业推荐师(官方)
  • 2026香辣火锅底料技术深度解析:麻辣牛油火锅底料/川味火锅底料/清汤火锅底料/清油火锅底料/番茄底料/红汤火锅底料/选择指南 - 优质品牌商家
  • 2026年北京中芯基业科技有限公司靠谱吗?口碑排名 - 工业品牌热点
  • 超深度测评!西安靠谱黄金回收门店单出炉 - 新闻快传
  • 跟着 MDN 学JavaScript day_4:如何存储你需要的信息——变量
  • 深度评测 2026 家电清洗培训,实力排名甄选优质培训班 - 湖南阳光技术
  • 从仿真到部署:基于快马平台实现工业级buck电源的实战开发
  • 2026苏州昆山实木全屋定制环保与工艺5家实力品牌测评排名:本地口碑推荐哪家好? - 新闻快传
  • 测评|杭州全屋定制企业做GEO应该怎么选服务商?靠谱GEO服务商推荐 - 新闻快传
  • 适合猫咪的宠物食品正规品牌排行 选购参考指南 - 互联网科技品牌测评
  • 酥必客靠谱吗,有保障吗? - 工业品牌热点
  • 2026年当下,武汉海绵门封供应商选哪家?服务商深度解析与选择指南 - 2026年企业资讯
  • 基于归一化流与Transformer的COVID-19预测模型
  • 超深度测评!北京靠谱黄金回收门店单出炉 - 新闻快传
  • LabelLLM:开源数据标注平台如何解决大模型训练中的标注难题?
  • 迎战2026年618电商节,实测产品不变形不走样的AI生图工具推荐 - 新闻快传
  • 快马平台快速生成ER图工具原型,三步搞定数据库可视化设计
  • 湛江代办许可证咨询指南:湛江社保公积金代办、/湛江财税政策解读/湛江财税服务/湛江一般纳税人记账怎么做/湛江代办许可证咨询电话多少/选择指南 - 优质品牌商家
  • 免费桌面伴侣终极指南:如何用Mate Engine打造你的专属虚拟伙伴
  • 2026年Q2岩棉板厂家技术选型实测与合规指南:成都夹芯岩棉板、成都岩棉保温板、成都岩棉复合板、成都岩棉板价格选择指南 - 优质品牌商家
  • 2026年成都校园文化建设公司评测:成都党建文化墙定制公司、成都公司前台形象墙设计公司、成都单位党建展厅施工公司选择指南 - 优质品牌商家
  • 杭州健身连锁店做GEO应该怎么选服务商?靠谱GEO服务商公司推荐? - 新闻快传
  • 2026年总氮标样口碑排名,云笈生物表现出色 - 工业品牌热点
  • 录播姬:开源免费的mikufans直播录制终极解决方案
  • 如何在所有Windows版本上使用Policy Plus进行高效组策略管理?
  • 天津老药丸回收首选!本草拾光,专业+上门双保障 - 深鉴新闻
  • 【数字营销人紧急避险手册】:3小时内修复AI文章重复率,绕过CSDN新版BERT+TF-IDF双模查重引擎
  • AI耳机哪个牌子好?EARWEISS听智慧凭硬核技术脱颖而出
  • 站外引流效果归因难题(CSDN官方埋点白皮书未披露的5个关键断点)
  • CSDN AI数字营销服务是否含站内广告?一线技术PM亲测的7个关键节点,错过将错失Q3流量红利