int similar_text ( string $string1 , string $string2 [, float &$percent ] )
$percent:參考傳遞,回傳**相似百分比**// 1. 僅取匹配字元數
echo similar_text('ly89cn', 'ly89'); // 4
// 2. 取得百分比
$percent = 0;
similar_text('ly89cn', 'ly89', $percent);
echo $percent; // 80
// 3. 中文字串(按位元組計算)
similar_text('王業樓的個人博客', '王業樓', $p);
echo $p; // 54.545... (UTF-8 每中文字 3 位元組,「王業樓」佔 9 位元組,總 18 位元組)
| 現象 | 原因 | 對策 |
|---|---|---|
| 相似度偏低或不符直覺 | 函數**逐位元組比較**,UTF-8 中文字佔 3 位元組,截斷會產生亂碼位元組 | 先轉為單字元陣列或使用 mb_str_split() (PHP 7.4+) 重新實作 LCS |
| 結果隨字串長度劇烈波動 | 演算法對長字串敏感,短字串一字差影響大 | 結合長度權重或改用 levenshtein() 配合自訂成本 |
| 大小寫敏感 | 直接比較位元組值 | 預先 strtolower() 或 mb_strtolower() |
similar_text vs levenshtein$a = '這是一段測試字串用來比較效能差異'; $b = '這是一段測試字串用來比較效能差別'; $start = microtime(true); for ($i=0; $i<1000; $i++) similar_text($a, $b); echo 'similar_text: ' . (microtime(true)-$start) . 's'; $start = microtime(true); for ($i=0; $i<1000; $i++) levenshtein($a, $b); echo 'levenshtein: ' . (microtime(true)-$start) . 's';
結論:levenshtein() **快 10-20 倍**,且支援自訂插入/刪除/替換成本,適合高頻呼叫場景。
function mb_similar_text($str1, $str2, &$percent = null) {
$arr1 = mb_str_split($str1); // PHP 7.4+,舊版用 preg_split('//u', $str1)
$arr2 = mb_str_split($str2);
// 簡易 LCS 動態規劃
$len1 = count($arr1);
$len2 = count($arr2);
$dp = array_fill(0, $len1+1, array_fill(0, $len2+1, 0));
for ($i=1; $i<=$len1; $i++) {
for ($j=1; $j<=$len2; $j++) {
if ($arr1[$i-1] === $arr2[$j-1]) {
$dp[$i][$j] = $dp[$i-1][$j-1] + 1;
} else {
$dp[$i][$j] = max($dp[$i-1][$j], $dp[$i][$j-1]);
}
}
}
$lcs = $dp[$len1][$len2];
if ($percent !== null) {
$percent = ($len1 + $len2) > 0 ? (2 * $lcs / ($len1 + $len2)) * 100 : 100;
}
return $lcs;
}
// 測試
$p = 0;
echo mb_similar_text('王業樓的個人博客', '王業樓', $p); // 3 (字元數)
echo $p; // 42.857...%
levenshtein() + 自訂成本(拼音權重)similar_text() 夠用,注意中文預處理jieba-php 分詞後算語義相似度