TypeScript:如何实现非连续单词数组中的句子检测?
非连续单词序列匹配的实现思路与代码
你的核心需求是让findText支持按顺序匹配非连续的单词——即搜索句的单词在数组中依次出现,但中间可夹杂其他单词,同时保留原有的文本清洗、别名匹配逻辑及返回格式规则。以下是具体实现思路和重构后的代码:
核心思路
原函数依赖连续切片匹配,无法处理非连续场景。要解决这个问题,需要改用多状态追踪的遍历方式,核心逻辑分为三步:
- 保留原有文本清洗、别名生成的基础工具逻辑;
- 遍历单词数组时,追踪所有可能的匹配分支(比如多个起始点的匹配进度);
- 收集所有完整匹配序列后,按原格式返回结果。
重构后的完整代码
function findText(searchStr: string, words: any[]) { const cleanEnding = (word: string) => { return word.replace(/[\s:;]*$/, ''); }; const cleanStart = (word: string) => { return word.replace(/^[\s]*/, ''); }; const getAliases = (word: string) => { return [word, word.replace('i', '1'), word.replace('i', 'l')]; }; // 预处理搜索字符串,过滤空词 searchStr = cleanEnding(searchStr); const splitSearch = searchStr.toLowerCase().split(" ").filter(word => word); if (splitSearch.length === 0) return null; // 预处理单词数组,保留原索引映射 const cleanedWords = words.map((w, idx) => ({ original: w, content: cleanStart(cleanEnding(w.content)).toLowerCase(), index: idx })); const fullMatches: number[][] = []; // 存储完整匹配的单词索引序列 let activeMatches: { currentSearchIdx: number; matchedIndices: number[] }[] = []; for (const cleanedWord of cleanedWords) { const wordAliases = getAliases(cleanedWord.content); const newActiveMatches = [...activeMatches]; // 复制分支避免遍历冲突 // 处理已有的活跃匹配分支 for (let i = 0; i < activeMatches.length; i++) { const match = activeMatches[i]; const targetSearchWord = splitSearch[match.currentSearchIdx]; // 检查当前单词与目标搜索词的别名匹配 if (wordAliases.includes(targetSearchWord) || getAliases(targetSearchWord).includes(cleanedWord.content)) { const newCurrentIdx = match.currentSearchIdx + 1; const newMatchedIndices = [...match.matchedIndices, cleanedWord.index]; if (newCurrentIdx === splitSearch.length) { // 匹配完成,加入结果集 fullMatches.push(newMatchedIndices); } else { // 更新分支匹配进度 newActiveMatches[i] = { currentSearchIdx: newCurrentIdx, matchedIndices: newMatchedIndices }; } } } // 检查是否可开启新的匹配分支(匹配第一个搜索词) const firstSearchWord = splitSearch[0]; if (wordAliases.includes(firstSearchWord) || getAliases(firstSearchWord).includes(cleanedWord.content)) { if (splitSearch.length === 1) { // 搜索词仅单个,直接加入结果 fullMatches.push([cleanedWord.index]); } else { // 开启新的匹配分支 newActiveMatches.push({ currentSearchIdx: 1, matchedIndices: [cleanedWord.index] }); } } activeMatches = newActiveMatches; } // 按原格式转换并返回结果 if (fullMatches.length === 0) { return null; } else if (fullMatches.length === 1) { return fullMatches[0].map(idx => words[idx]); } else { return fullMatches.map(indices => indices.map(idx => words[idx])); } }
功能验证
用你的测试数组验证:
- 调用
findText("Date of his", words):仍返回连续匹配的[{id:5, content:'Date'}, {id:6, content:'of'}, {id:7, content:'his'}]; - 调用
findText("Date Birthday", words):返回两个非连续匹配序列[[{id:1, content:'Date'}, {id:4, content:'Birthday'}], [{id:5, content:'Date'}, {id:8, content:'Birthday'}]]; - 调用
findText("Date of abc", words):返回null; - 调用
findText("Date of", words):返回两个连续匹配序列,若数组中存在非连续的Date和of(中间夹其他词),也会被正确匹配。
内容的提问来源于stack exchange,提问作者Dylan Grum's
相关产品推荐
相关产品推荐

