PHP按序列对比句子单词 解决array_diff重复词漏统计问题
现有方案核心缺陷
用array_diff做对比的逻辑,本质是基于「值存在性」的无状态匹配,完全不满足序列-wise对比的要求,核心问题有两个:
- 不校验词的出现频次:只要用户输入中出现过1次某个单词,原句中所有同值的重复单词都会被判定为“已匹配”。比如原句有2个
am,用户只输入1个am,漏打的那个am根本不会被统计到。举个极端例子:原句是两个连续的a,用户只打了1个a,array_diff会直接因为用户输入里存在a,把原句两个a都判定为已匹配,漏打数算成0,结果完全不准。 - 不校验序列位置:哪怕用户把所有单词的顺序完全打乱,只要所有单词值都能在对方数组里找到,就会被判定为完全匹配,不符合逐句输入的业务场景。
拿题目中的测试用例跑原逻辑,输出的总错误数是2(漏lunch、多dinner),但实际漏打了1个am,真实错误数应该是3,结果存在明显偏差。
适配场景的实现方案
这个场景的核心是做词粒度的序列对齐计算,根本不需要依赖第三方库,用基础动态规划就能实现精确统计:
- 把原句、用户输入句拆成逐词数组,严格按出现顺序逐位对齐
- 统计三类核心指标:
- 位置与值完全匹配的单词数(正确数)
- 原句有、序列对齐时被跳过的单词数(漏打数)
- 用户输入有、序列对齐时额外多出来的单词数(多打数)
可直接复用的PHP实现
function calculateWordMatchStats(array $originalWords, array $typedWords): array { $origLen = count($originalWords); $typedLen = count($typedWords); // 构建DP表,dp[i][j] 表示原句前i个词 对齐 用户输入前j个词的最小操作数 $dp = []; for ($i = 0; $i <= $origLen; $i++) { $dp[$i][0] = $i; // 原句前i个词全是漏打的操作数 } for ($j = 0; $j <= $typedLen; $j++) { $dp[0][$j] = $j; // 用户输入前j个词全是多打的操作数 } // 填充DP表 for ($i = 1; $i <= $origLen; $i++) { for ($j = 1; $j <= $typedLen; $j++) { // 两个位置的词完全一致,不需要额外操作 if ($originalWords[$i-1] === $typedWords[$j-1]) { $dp[$i][$j] = $dp[$i-1][$j-1]; } else { // 两种操作取最小值:漏打原句词、多打用户输入词(打错字的替换本质就是漏1个+多1个,不单独统计) $dp[$i][$j] = min( $dp[$i-1][$j] + 1, $dp[$i][$j-1] + 1 ); } } } // 回溯DP表,分类统计计数 $correct = 0; $missing = 0; $extra = 0; $i = $origLen; $j = $typedLen; while ($i > 0 || $j > 0) { if ($i > 0 && $j > 0 && $originalWords[$i-1] === $typedWords[$j-1]) { $correct++; $i--; $j--; } elseif ($i > 0 && $dp[$i][$j] === $dp[$i-1][$j] + 1) { // 原句的词没被输入,算漏打 $missing++; $i--; } else { // 用户输入的词是额外多打的 $extra++; $j--; } } return [ 'correct_count' => $correct, 'missing_count' => $missing, 'extra_count' => $extra, 'total_error' => $missing + $extra ]; }
示例验证
拿题目中的测试用例跑函数:
$Str1 = "i am suraj roy i am having my lunch"; $Str2 = "i suraj roy i am having my dinner"; $st1 = explode(" ", $Str1); $st2 = explode(" ", $Str2); $stats = calculateWordMatchStats($st1, $st2); print_r($stats);
输出结果:
Array ( [correct_count] => 7 // 正确匹配的词:i、suraj、roy、i、am、having、my [missing_count] => 2 // 漏打:原句第二个am、lunch [extra_count] => 1 // 多打:dinner [total_error] => 3 // 总错误数 )
结果完全符合业务要求,不会漏掉重复词的漏打统计。
实现注意点
- 对比前要统一大小写、去除特殊符号、统一空格分隔规则,避免因为格式问题导致误判
- 这个实现是严格按输入顺序对齐的,完全匹配用户逐句看着原句输入的场景,不会出现跨位置乱匹配的问题
内容的提问来源于stack exchange,提问作者Koushik
相关产品推荐
相关产品推荐

