如何在单循环约束下实现用户表情计数及无计数用户的继承逻辑?
如何在单循环约束下实现用户表情计数及无计数用户的继承逻辑?
嘿,我看了你的问题和代码,问题出在你现在的逻辑是遇到下一个用户就立刻把当前用户的临时计数存进去,但没考虑到无计数用户要继承后续用户计数的规则——比如Max后面没有自己的苹果表情,得继承Alisa的2个,但你的代码直接把Max的计数设成了0。
要解决这个问题,同时满足单循环、只用一个对象的约束,我们可以换个遍历方向:从字符串末尾往前遍历。这样继承逻辑就变成了“正向传递”——后面的用户(原顺序)的计数可以直接传给前面的(原顺序)无计数用户,完美贴合题目要求的继承规则。
下面是调整后的完整代码,完全符合所有约束条件:
function countEmoji(message, emoji) { const ASCII = { numFirstCharCode: 48, numLastCharCode: 57, openBracket: 60, closeBracket: 62, atSign: 64, slash: 47, toLowerOffest: 32, upperCaseStart: 65, upperCaseEnd: 90, lowerCaseStart: 97, lowerCaseEnd: 122, space: 32, colon: 58, }; let result = {}; let currentUserName = ""; let nameStage = 0; // 0: 寻找<, 1: 找到<后找@, 2: 收集用户名, 3: 找/, 4: 找> let emojiCount = 0; let emojiStage = -1; // -1:未开始匹配, 0~emoji.length-1:匹配中, -2:匹配完emoji等待: let currentInheritCount = 0; function toLowerCase(charCode) { const isInUpperCase = ASCII.upperCaseStart <= charCode && charCode <= ASCII.upperCaseEnd; if (isInUpperCase) { return String.fromCharCode(charCode + ASCII.toLowerOffest); } return String.fromCharCode(charCode); } // 从字符串末尾向前遍历 for (let i = message.length - 1; i >= 0; i--) { const charCode = message.charCodeAt(i); const isUpper = charCode >= ASCII.upperCaseStart && charCode <= ASCII.upperCaseEnd; const isLower = charCode >= ASCII.lowerCaseStart && charCode <= ASCII.lowerCaseEnd; const isLetter = isUpper || isLower; // 优先处理用户识别逻辑 if (nameStage !== 0) { switch(nameStage) { case 1: // 找到<后,寻找@ if (charCode === ASCII.atSign) { nameStage = 2; currentUserName = ""; } break; case 2: // 收集用户名(反向遍历,字符要加到前面保证顺序正确) if (isLetter) { const lowerChar = isUpper ? String.fromCharCode(charCode + ASCII.toLowerOffest) : String.fromCharCode(charCode); currentUserName = lowerChar + currentUserName; } else if (charCode === ASCII.space) { // 忽略用户名后的空格 continue; } else if (charCode === ASCII.slash) { nameStage = 3; } break; case 3: // 找到/后,寻找> if (charCode === ASCII.closeBracket) { // 用户名收集完成,处理计数 if (emojiCount > 0) { currentInheritCount = emojiCount; result[currentUserName] = emojiCount; } else { result[currentUserName] = currentInheritCount; } // 重置状态 emojiCount = 0; currentUserName = ""; nameStage = 0; } else if (charCode === ASCII.space) { // 忽略/和>之间的空格 continue; } break; } continue; } // 寻找用户起始标记<,开启用户识别流程 if (charCode === ASCII.openBracket) { nameStage = 1; continue; } // 处理表情识别逻辑 if (charCode === ASCII.colon) { if (emojiStage === -1) { // 找到表情起始:,开始匹配emoji的最后一个字符 emojiStage = emoji.length - 1; } else if (emojiStage === -2) { // 完成完整表情匹配,计数+1 emojiCount++; emojiStage = -1; } continue; } // 正在匹配emoji字符 if (emojiStage >= 0) { const emojiCharLower = toLowerCase(emoji.charCodeAt(emojiStage)); const currentCharLower = toLowerCase(charCode); if (currentCharLower === emojiCharLower) { emojiStage--; if (emojiStage === -1) { // 匹配完所有emoji字符,等待结束: emojiStage = -2; } } else { // 匹配失败,重置状态 emojiStage = -1; } } } return result; } const text = '<@Kate />:apple: <@Max/>sometext<@alisa /> :like: received:apple::apple:'; const emoji = "apple"; console.log(countEmoji(text, emoji)); // 输出 { kate: 1, max: 2, alisa: 2 }
核心逻辑说明
- 反向遍历:从字符串最后一个字符开始往前遍历,这样我们能先处理原顺序中靠后的用户,把他们的计数作为继承源传递给前面的用户。
- 用户识别调整:原用户格式是
<@Name />,反向看就是/> Name @<,所以我们调整识别步骤:先找<,再找@,然后收集用户名(注意收集时把每个字符加到前面,保证用户名顺序正确),最后找/和>完成识别。 - 表情识别调整:原表情是
:apple:,反向看是:elppa:,我们不需要反转emoji字符串,而是直接从emoji的最后一个字符开始匹配,匹配完成后等待下一个:来确认有效表情。 - 继承逻辑实现:维护一个
currentInheritCount变量,记录最近一个有有效表情计数的用户的数值。当识别到一个用户时:- 如果该用户有自己的表情计数,就更新
currentInheritCount为这个数值,并存入结果 - 如果该用户没有表情计数,就直接用
currentInheritCount作为他的计数
- 如果该用户有自己的表情计数,就更新
为什么你的原代码出错?
你原代码是遇到新用户就把当前用户的临时计数(当时是0)存入结果,但这个临时计数是还没考虑后续用户的继承值。反向遍历让我们先拿到后续用户的有效计数,再给前面的无计数用户赋值,完美解决了继承链的问题。
备注:内容来源于stack exchange,提问作者vadim
相关产品推荐
相关产品推荐

