Tạo các biến thể sai chính tả của một từ bất kỳ có thể giúp ích trong việc phát hiện và sửa lỗi

Xử lý lỗi chính tả tên người chúng ta cần xử lý các trường hợp đặc thù trước.

Lỗi chính tả tên người rất phong phú và đa dạng, bắt lỗi có thể không quá khó, nhưng sửa như thế nào lại là vấn đề, vì tùy tình huống bối cảnh khác nhau mà cách xử lý cũng không thể giống nhau được.

Trong lỗi chính tả có những trường hợp đặc thù mà chúng ta cần xử lý trước, vì các trường hợp như vậy việc nhận biết và hướng giải quyết cũng đơn giản hơn, chẳng hạn như:

Trong bài viết ngày hôm nay tôi sẽ nói về việc chế hàm tạo tự động các biến thể sai chính tả của một từ nào đó.

Các biến thể sai chính tả do gõ nhầm có thể bao gồm các trường hợp sau:

  • Thiếu một ký tự trong họ tên, ví dụ thay vì nguyễn thì là ngyễn;
  • Thừa một ký tự trong họ tên, ví dụ nguyễnn
  • Ký tự bị đảo vị trí, ví dụ như ngyuễn
  • Thiếu dấu thanh trong các từ có dấu thanh, ví dụ nguyên
  • Thiếu hoàn toàn dấu thanh và dấu mũ, ví dụ nguyen
  • Đặt sai vị trí dấu thanh, ví dụ ngũyen
  • Vị trí dấu thanh đúng, nhưng dấu thanh lại sai, ví dụ nguyển, cái này rất hay xảy ra với dấu hỏi, ngã và huyền, vì nó nhìn khá giống nhau, ít khi xảy ra với dấu sắc và dấu nặng;
  • Một ký tự lạ kèm với họ tên, ví dụ như Tuấnz

Việc xác định lỗi chính tả với họ thì dễ hơn so với tên hoặc đệm, vì họ có trường dữ liệu ổn định, còn tên, đệm thì rất phong phú, về lý thuyết có thể là bất cứ từ nào, thậm chí còn đa dạng hơn văn bản thông thường, vì tên người không có chuẩn chính tả thống nhất được như văn bản- tên người tuân thủ theo quy tắc văn hóa nhiều hơn.

Tên người cũng là dữ liệu rất ngắn gọn, thường chỉ 2 đến 4 từ với khoảng từ 5 đến 20 ký tự, dữ liệu nếu so với văn bản là rất nhỏ. Ví dụ một bài báo có thể có hàng ngàn từ để chúng ta phân tích.

Tuy nhiên họ tên cũng có rất nhiều luật ngầm ẩn mà chúng ta có thể căn cứ vào đó để xử lý hiệu quả hơn, chẳng hạn:

  • Chúng ta có số lượng họ nhất định, và gần như cố định;
  • Tên người dù có thể là bất cứ từ nào nhưng nó cũng rất tập trung, 500 tên phổ biến có thể chiếm đến hơn 95% số tên thực tế được đặt;
  • Tên thường có khuynh hướng nhắm đến ý nghĩa tốt đẹp, nên ý nghĩa xấu có khả năng cao loại trừ, đặc biệt là tên người trẻ;

Tôi sẽ nói về thuật toán xử lý họ sai chính tả trước.

  • Kiểm tra họ của một người có trong danh sách họ thực tế không. Nếu không họ đó có thể là viết sai chính tả;
  • Kiểm tra xem họ sai chính tả đó gần với các họ đúng nào? Ví dụ nguye có thể xem là gần với cả họ nguyễn lẫn ngụy, nhưng khả năng cao là nguyễn nhiều hơn vì có âm e ở cuối. Tuy nhiên nếu văn bản đó là ngụye thì có thể chúng ta không chắc được đây là họ nào;
  • Nếu mức độ tương tự của một họ sai chính tả với từ 2 họ đúng chính tả rất tương đồng thì sẽ rất khó để chúng ta xác định được đâu là họ đúng; Ví dụ Pham có thể là Phan mà cũng có thể là Phạm, trên bàn phím, ký tự mn sát nhau, không chỉ có thế kiểu hình của nó cũng khá tương đồng;
  • Nếu mức độ chênh lệch là vài lần, trường hợp này sẽ dễ quyết định hơn, mức chênh càng lớn càng dễ chắc chắn;
  • Cần phân biệt sai chính tả với viết tắt, một trong những từ viết tắt họ tên người phổ biến nhất là ng chỉ cho họ nguyễn. Trong văn bản tên người, ký hiệu viết tắt này hay xuất hiện ở các tên dài 4 từ đổ lên;

Vậy điểm mấu chốt ở đây là chúng ta phải xác định được thuật toán đánh giá mức độ tương đồng giữa họ sai chính tả và họ tồn tại thực tế có khả năng là họ chính xác.

PHP có một số hàm để xác định mức độ tương đồng của 2 chuỗi, khả quan nhất là similar_textlevenshtein (những hàm còn lại liên quan đến ngữ âm tiếng Anh, chứ không phải thuần túy văn bản nên không phù hợp với tiếng Việt).

Thuật toán của levenshtein dễ hiểu hơn, nó đo số thao tác thêm, xóa và thay thế để biến chuỗi thứ nhất thành chuỗi thứ hai, giá trị này càng nhỏ thì càng tương đồng. Ví dụ giữa nguyễnguyễn là 1, vì chỉ cần thêm chữ n nữa (một thao tác) là hai chuỗi hoàn toàn giống nhau.

similar_text thì tính theo %, mức độ tương đồng của ví dụ trên có giá trị hơn 93%.

echo levenshtein("nguyễ","nguyễn"); // kết quả là 1
echo "</br>";
similar_text("nguyễ", "nguyễn",$p);
echo $p; // kết quả là 93,3%

Còn khi so với ngụy thì sao:

echo levenshtein("nguyễ","nguỵ"); // kết quả là 2
echo "</br>";
similar_text("nguyễ", "nguỵ",$p);
echo $p; // kết quả là 76,9%

1 thì tốt hơn 2 (với levenshtein giá trị càng nhỏ càng tương đồng), còn với similar_text, càng lớn càng tốt, ở đây 93,3% tốt hơn. Như vậy kết quả trường hợp này rất rõ ràng.

Ví dụ khác:

echo levenshtein("nguyen","nguyễn"); // kết quả là 3
echo "</br>";
similar_text("nguyen", "nguyễn",$p);
echo $p; // kết quả là 71,5%
echo levenshtein("nguyen","nguỵ"); // kết quả là 3
echo "</br>";
similar_text("nguyen", "nguỵ",$p);
echo $p; // kết quả là 50%

Trong trường hợp này kết luận của similar_text gần với thực tế hơn, vì nguyen gần với nguyễn hơn nhiều so với nguyen gần với ngụy. Kết quả của levenshtein đánh giá chúng ngang nhau.

Cũng có trường hợp levenshtein dự đoán chính xác hơn. Chuỗi càng ngắn khả năng levenshtein sẽ dự đoán càng sai, vì chuỗi ngắn, mức độ lệch chỉ 1 ký tự sẽ ra nghĩa khác nhiều hơn so với chuỗi dài.

Tuy nhiên cả 2 hàm được thiết kế không tính đến chuyện đánh giá ngữ nghĩa, các ký tự phím gần nhau, và tất nhiên không tính đến cả việc kiểu gõ tiếng Việt tạo ra đặc thù riêng trong sai chính tả nhập liệu.

Với giả định như vậy, chúng ta sẽ viết một hàm ngược, tức là đưa các yếu tốt bàn phím vào và kiểu gõ vào rồi đối sánh dữ liệu thực tế xem có đúng là các lỗi đấy xuất hiện thường xuyên hơn không.

Chẳng hạn xuyww (phím w gần với e) nếu tính đến bàn phím và kiểu gõ telex nó sẽ gần với xuyên nhiều so với xuy đáng kể.

Nhưng với 2 hàm trên, kết quả sẽ ngược lại:

echo levenshtein("xuyww","xuy"); // kết quả là 2
echo "</br>";
similar_text("xuyww", "xuy",$p);
echo $p; // kết quả là 75


echo levenshtein("xuyww","xuyên"); // kết quả là 3
echo "</br>";
similar_text("xuyww", "xuyên",$p);
echo $p; // kết quả là 54,5%

Nhưng tất cả hẵn là dự đoán, vì mẫu sai chính tả mà tôi hiện có còn ít, giờ chúng ta sẽ tìm nó bằng cách thử mọi trường hợp có thể xem thế nào, rồi may ra mới có kết luận chắc chắn hơn được.

Các đoạn mã

A. Bớt một ký tự:

$str = "nguyễn"; // mẫu đầu vào ví dụ là nguyễn
    
$tt = preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY); // cắt chuỗi
$ct = count($tt); // số phần tử trong chuỗi

$bot_kt = array(); // mảng chứa chuỗi bớt một ký tự từ chuỗi chuẩn $str

for ($i=0; $i<$ct; $i++) {
        $kp = $tt[$i];
        $tt[$i] = ''; // bỏ nó
        $tg = ''; // gọi là trung gian vì sau khi thực hiện xong nhiệm vụ, nó không cần quan tâm đến nữa
              for ($j=0; $j<$ct; $j++) {$tg.=$tt[$j];}

        $bot_kt[$i]=$tg; // đưa kết quả có được vào mảng bot
        $tt[$i]=$kp;//khôi phục lại ký tự thứ i để vòng lặp sau nó không bị mất giá trị (vòng lặp sau chúng ta xóa ký tự thứ i + 1)
}

Kết quả nó tự động tạo các chuỗi đầu ra sau:

guyễn
nuyễn
ngyễn
nguễn
nguyn
nguyễ

B. Thêm một ký tự đôi (ví nnguyễn, ngguyễn):

$them_kt = array(); // thêm một ký tự trong chuỗi

for ($i=0; $i<$ct; $i++) {
        $kp = $tt[$i];
        $tt[$i] = $kp.$kp;
        $tg = '';
              for ($j=0; $j<$ct; $j++) {$tg.=$tt[$j];}
        $them_kt[$i] = $tg;
        $tt[$i] = $kp;//khôi phục lại
}

Kết quả của giải pháp trên:

nnguyễn
ngguyễn
nguuyễn
nguyyễn
nguyễễn
nguyễnn

C. Đảo vị trí 2 ký tự liền nhau

$dao_kt=array(); // đảo vị trí 2 ký tự ở cạnh nhau

$cg = $ct-1; // phải trừ 1 vì vị trí cuối cùng thì không phải đảo nữa
for ($i=0; $i<$cg; $i++) {
        $ai = $tt[$i];
        $k = $i+1; // ký tự liền sau nên là i + 1
        $ak = $tt[$k];
        
        // đảo giá trị cho nhau
        
        $tt[$i]=$ak; 
        $tt[$k]=$ai;
        
        $tg='';
              for ($j=0;$j<$ct;$j++) {$tg.=$tt[$j];}
        $dao_kt[$i]=$tg;
        
        $tt[$i]=$ai;//khôi phục lại
        $tt[$k]=$ak;// để vòng lặp sau không bị ảnh hưởng
}

Kết quả:

gnuyễn
nugyễn
ngyuễn
nguễyn
nguynễ

3 phần đầu liên quan đến trật tự từ đơn giản hơn tôi nghĩ, tưởng cứ phải viết gì phức tạp quá cơ, hóa ra chỉ cần dùng vòng lặp for là đã giải quyết được. Giờ chúng ta viết các lỗi liên quan đến ký tự.

D. Xóa dấu của ký tự

Tôi viết hàm xóa dấu riêng, vì cái này còn dùng vào việc khác.

function xoa_dau($str){ // xóa dấu của một ký tự hoặc chuỗi
    $dx = array("cc80","cc81","cc83","cc89","cca3"); // mã hóa dấu tiếng Việt
    $hz = bin2hex(mahoa_itdung($str)); // chuyển sang mã hex để tìm dấu
    
    $i=0;

foreach ($dx as $dy) { 
             $dz ='/'.$dy.'/';
                  if (preg_match($dz, $hz)) {
                      $hz = preg_replace($dz,'',$hz); // khử dấu của $hz; nó vẫn đang ở dạng hex
                      $i++;
                  } 
    }

    $kq = chuyen_ma_hoa(hex2bin($hz)); 

return $kq;
}

// không dấu
$khongdau = xoa_dau($str);

// Kết quả: nguyên

Trước khi xóa dấu để đỡ mất công, chúng ta nên kiểm tra trước là họ hay tên có dấu không đã bằng hàm sau:

function sl_co_dau($str) { // tìm số lượng từ có dấu trong chuỗi, chỉ nên áp dụng cho một từ
$cd = mang_codau();
$sl_cd = 0;
           foreach ($cd as $cd2) {
               $cd3='/'.$cd2.'/';
               if (preg_match($cd3, $str)) {$sl_cd++;}
           }
return $sl_cd;           
}

Bạn lưu ý là hàm này chỉ xóa dấu, chứ không xóa mũ như là ê thành e, hay ơ thành o, ngay sau đây chúng ta mới viết hàm như vậy.

E. Bỏ cả dấu và mũ

// bỏ mũ
$bmm = preg_split('//u', $khongdau, -1, PREG_SPLIT_NO_EMPTY); // cắt chuỗi
$abomu = array(); $j=0; $bomu="";

foreach ($bmm as $bmm2) {
         if ($bmm2 == "ă" || $bmm2 == "â") {$bmm2="a";}
         if ($bmm2 == "ô" || $bmm2 == "ơ") {$bmm2="o";}
         if ($bmm2 == "ê") {$bmm2="e";}
         if ($bmm2 == "ư") {$bmm2="u";}
         $abomu[$j] = $bmm2; $j++;
}

foreach ($abomu as $bbomu) {
       $bomu.=$bbomu;
}

// Kết quả: nguyen

F. Đổi sang dấu thanh khác, nhưng giữ nguyên vị trí dấu

// thêm các dấu vào từ

        $dau = tim_dau($str);

        $kt = catn_kt($dau, 1);
        $vt = (int)(1 + catn_kt($dau, -1));
        $dod = array(); $j = 0;

        if ($vt>1) {
                    $m = them_dau($kt, 6); // thêm tất cả các biến thể của dấu vào ký tự

                    foreach ($m as $tt) {
                        $tg = thay_the($str, $tt, $vt);
                        if ($tg!=$str) {$dod[$j] = $tg; $j++;}
                    }

        }

Mấy hàm tim_dau, catn_kt,.. là tôi viết riêng, bạn có thể tham khảo sửa đổi cũng như cập nhật của nó ở bài này.

Kết quả:

nguyền
nguyến
nguyển
nguyện
nguyễn // cái này trùng với tên gốc, chúng ta sẽ loại bỏ nó sau

G. Thêm dấu cho các nguyên âm khác trong từ đã bỏ hoàn toàn dấu và mũ

Ví dụ nguỹen hoặc ngũyen.

Ở đây 5 dấu được bổ sung vào các nguyên âm đã bỏ hết dấu và mũ (tức đầu vào là chuỗi $bomu, ví dụ nguyen).

        $dau = di_tim_dau($str);

        $kt = catn_kt($dau, 1);
        $vtx = catn_kt($dau, -1);
        $vt = (int)($vtx); 
        $dod = array(); $j = 0;

        if ($vt>0) {
                    $m = them_dau($kt, 6); // thêm tất cả các biến thể có dấu vào ký tự

                    foreach ($m as $tx) {
                        $tg = thay_the($str, $tx, $vt);
                        $dod[$j] = $tg; $j++;
                    }
                      $j++; // bổ sung thêm từ không dấu
                    $dod[$j] = $khongdau;
                      $j++; // bổ sung thêm từ không dấu, không mũ
                    $dod[$j] = $bomu;
        }
  
 // thêm dấu vào các nguyên âm trong từ gốc hiện không có dấu
        
 // lấy mảng các ký tự không dấu a, ô, ư, vân vân ra để đối chiếu
        
        $mkd = mang_khongdau(); $i=0;
        $mthemdau=array(); $j=0;// tạo mảng để chứa các ký tự bổ sung dấu
        $tt2 = preg_split('//u', $bomu, -1, PREG_SPLIT_NO_EMPTY); // cắt chuỗi
        
        foreach ($mkd as $mkd2) { // bắn ra các nguyên âm không dấu
                   foreach ($tt2 as $tt3) { // bắn ra các ký tự của chuỗi
                               if ($tt3 == $mkd2) { // so sánh ký tự của chuỗi xem nó có các nguyên âm nào, nếu có thì gắn thêm dấu vào
                                         $vt = vi_tri($bomu, $tt3);
                                         $td = them_dau($tt3, 6); // thêm 5 dấu cho nguyên âm
                                                                    foreach ($td as $tk) { // bắn ra các ký tự đã thêm dấu
                                                                                $tg2 = thay_the($bomu, $tk, $vt[0]); // thay thế ký tự đó vào chuỗi gốc
                                                                                
                                                                                $mthemdau[$j] = $tg2; $j++; // thêm ký tự đó vào mảng
                                                                    }
                               }
                   }                
        }

Kết quả:

nguyèn
nguyén
nguyẽn
nguyẻn
nguyẹn
ngùyen
ngúyen
ngũyen
ngủyen
ngụyen
nguỳen
nguýen
nguỹen
nguỷen
nguỵen

G. Thêm dấu cho các nguyên âm khác trong từ bỏ dấu nhưng vẫn còn mũ

Ví dụ ngũyên hoặc nguỹên.

Về bản chất mã đoạn này hệt như ở phần G, chỉ khác là đầu vào là chuỗi vẫn còn mũ. Bạn để đầu vào là chuỗi không dấu của chuỗi gốc là OK. Kết quả:

nguyền
nguyến
nguyễn
nguyển
nguyện
ngùyên
ngúyên
ngũyên
ngủyên
ngụyên
nguỳên
nguýên
nguỹên
nguỷên
nguỵên

Tiếp đến chúng ta tiến hành gộp các mảng lại với nhau và tiến hành loại bỏ các giá trị trùng lặp:

$kq = array_merge($bot_kt,$them_kt,$dao_kt,$dod,$mthemdau); // gộp các mảng vảo với nhau

$ukq = array_unique($kq); // loại bỏ các giá trị trùng lặp

Kết quả tổng hợp các đoạn mã trên giúp xuất ra được dữ liệu sai chính tả như thế này:

guyễn
nuyễn
ngyễn
nguễn
nguyn
nguyễ
nnguyễn
ngguyễn
nguuyễn
nguyyễn
nguyễễn
nguyễnn
gnuyễn
nugyễn
ngyuễn
nguễyn
nguynễ
nguyền
nguyến
nguyển
nguyện
nguyên
nguyen
nguyèn
nguyén
nguyẽn
nguyẻn
nguyẹn
ngùyen
ngúyen
ngũyen
ngủyen
ngụyen
nguỳen
nguýen
nguỹen
nguỷen
nguỵen
ngùyên
ngúyên
ngũyên
ngủyên
ngụyên
nguỳên
nguýên
nguỹên
nguỷên
nguỵên

Ở đây chúng ta thấy có nhiều từ có khả năng cao sẽ là từ sai chính tả trong thực tế (ví dụ nguyển), nhưng cũng có từ có khả năng rất thấp (ví dụ ngủyên hoặc nguyễễn). Nhưng không sao, chúng ta chỉ đang cố tạo ra càng nhiều trường hợp càng tốt để đối sánh. Và sẽ điều chỉnh sau nếu thấy cách thức nào đó không phù hợp.

Đoạn mã trên có thể bổ sung thêm bằng cách chuyển các từ có dấu hoặc/và mũ về kiểu gõ telex sai chính tả, ví dụ nguyeenx hoặc nguyeen. Công đoạn cuối là ta sẽ phải lọc danh sách trên để loại các từ trùng với danh sách họ hiện có.

Mã hoàn chỉnh:

function saichinhta_ho($str) {

$tt = preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY); // cắt chuỗi
$ct = count($tt); // số phần tử trong chuỗi    

        // bỏ một ký tự trong từ
        $bot_kt=array(); // mảng chứa chuỗi bớt một ký tự từ chuỗi chuẩn $str  

        if ($ct>3) {
        // yêu cầu số lượng ký tự tối thiểu là 3 thì mới thực hiện việc chuyển đổi này
        // nếu từ 3 trở xuống mảng trả về là rỗng    
            for ($i=0; $i<$ct; $i++) {
                    $kp = $tt[$i];
                    $tt[$i] = ''; // bỏ nó
                    $tg = ''; // gọi là trung gian vì sau khi thực hiện xong nhiệm vụ, nó không cần quan tâm đến nữa
                          for ($j=0; $j<$ct; $j++) {$tg.=$tt[$j];}

                    $bot_kt[$i]=$tg; // đưa kết quả có được vào mảng bot
                    $tt[$i]=$kp;//khôi phục lại để vòng lặp sau nó không bị mất giá trị
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////

        // nhân đôi một ký tự
        $them_kt = array(); // thêm một ký tự trong chuỗi
                for ($i=0; $i<$ct; $i++) {
                        $kp = $tt[$i];
                        $tt[$i] = $kp.$kp;
                        $tg = '';
                              for ($j=0; $j<$ct; $j++) {$tg.=$tt[$j];}
                        $them_kt[$i] = $tg;
                        $tt[$i] = $kp;//khôi phục lại
                }


        ////////////////////////////////////////////////////////////////////////////////////

        // đảo ký tự

        $dao_kt=array(); // đảo vị trí 2 ký tự ở cạnh nhau

            $cg = $ct-1; // phải trừ 1 vì vị trí cuối cùng thì không phải đảo nữa
            for ($i=0; $i<$cg; $i++) {
                    $ai = $tt[$i];
                    $k = $i+1; // ký tự liền sau nên là i + 1
                    $ak = $tt[$k];

                    // đảo giá trị cho nhau

                    $tt[$i]=$ak; 
                    $tt[$k]=$ai;

                    $tg='';
                          for ($j=0;$j<$ct;$j++) {$tg.=$tt[$j];}
                    $dao_kt[$i]=$tg;

                    $tt[$i]=$ai;//khôi phục lại
                    $tt[$k]=$ak;// để vòng lặp sau không bị ảnh hưởng
            }


        ////////////////////////////////////////////////////////////////////////////

        // Không dấu
        $khong_dau  = xoa_dau($str);

        //////////////////////////////////////////////////////////////////////////////

        $bomu = $khong_dau;
        $bmm = preg_split('//u', $bomu, -1, PREG_SPLIT_NO_EMPTY); // cắt chuỗi
        $abomu = array(); $j=0; $bomu="";

        foreach ($bmm as $bmm2) {
                 if ($bmm2 == "ă" || $bmm2 == "â") {$bmm2="a";}
                 if ($bmm2 == "ô" || $bmm2 == "ơ") {$bmm2="o";}
                 if ($bmm2 == "ê") {$bmm2="e";}
                 if ($bmm2 == "ư") {$bmm2="u";}
                 $abomu[$j] = $bmm2; $j++;
        }

        if (count($abomu)) {
                            foreach ($abomu as $bbomu) {
                                   $bomu.=$bbomu;
        }}

        ///////////////////////////////////////////////////////////////////////////////////////
        // thêm các dấu vào từ ban đầu đã có dấu

                $themdau2 = array(); $j = 0;

                $dau = di_tim_dau($str);
                $kt = catn_kt($dau, 1);
                $vtx = catn_kt($dau, -1);
                $vt = (int)($vtx); // mới ra vị trí thực trong mảng

                if ($vt>0) {
                            $m = them_dau($kt, 6); // thêm tất cả các biến thể có dấu vào ký tự

                            foreach ($m as $tx) {
                                $tg = thay_the($str, $tx, $vt);
                                $themdau2[$j] = $tg; $j++;
                            }
                              $j++; // bổ sung thêm từ không dấu
                            $themdau2[$j] = $khong_dau;
                              $j++; // bổ sung thêm từ không dấu, không mũ
                            $themdau2[$j] = $bomu;
                }


        //////////////////////////////////////////////////////////////
         // thêm dấu vào các nguyên âm trong từ gốc hiện không có dấu

         // lấy mảng các ký tự không dấu a, ô, ư, vân vân ra để đối chiếu


                $mkd = mang_khongdau(); $i=0;
                $themdau3=array(); $j=0;// tạo mảng để chứa các ký tự bổ sung dấu
                $tt2 = preg_split('//u', $bomu, -1, PREG_SPLIT_NO_EMPTY); // cắt chuỗi

                foreach ($mkd as $mkd2) { // bắn ra các nguyên âm không dấu
                           foreach ($tt2 as $tt3) { // bắn ra các ký tự của chuỗi
                                       if ($tt3 == $mkd2) { // so sánh ký tự của chuỗi xem nó có các nguyên âm nào, nếu có thì gắn thêm dấu vào
                                                 $vt = vi_tri($bomu, $tt3);
                                                 $td = them_dau($tt3, 6); // thêm 5 dấu cho nguyên âm
                                                                            foreach ($td as $tk) { // bắn ra các ký tự đã thêm dấu
                                                                                        $tg2 = thay_the($bomu, $tk, $vt[0]); // thay thế ký tự đó vào chuỗi gốc

                                                                                        $themdau3[$j] = $tg2; $j++; // thêm ký tự đó vào mảng
                                                                            }
                                       }
                           }                
                }

        ///////////////////////////////////////////////////////////////////////////
         // thêm dấu cho nguyên âm khác trong từ vẫn còn mũ

                $khongdau2 = xoa_dau($str);
                $themdau4=array(); $i=0; $j=0;// tạo mảng để chứa các ký tự bổ sung dấu
                $tt2x = preg_split('//u', $khongdau2, -1, PREG_SPLIT_NO_EMPTY); // cắt chuỗi

                foreach ($mkd as $mkd2x) { // bắn ra các nguyên âm không dấu
                           foreach ($tt2x as $tt3x) { // bắn ra các ký tự của chuỗi
                                       if ($tt3x == $mkd2x) { // so sánh ký tự của chuỗi xem nó có các nguyên âm nào, nếu có thì gắn thêm dấu vào
                                                 $vtx = vi_tri($khongdau2, $tt3x);
                                                 $tdx = them_dau($tt3x, 6); // thêm 5 dấu cho nguyên âm
                                                                            foreach ($tdx as $tkx) { // bắn ra các ký tự đã thêm dấu
                                                                                        $tg2x = thay_the($khongdau2, $tkx, $vtx[0]); // thay thế ký tự đó vào chuỗi gốc

                                                                                        $themdau4[$j] = $tg2x; $j++; // thêm ký tự đó vào mảng
                                                                            }
                                       }
                           }                
                }     

        ///////////////////////////

        $kq = array_merge($bot_kt,$them_kt,$dao_kt,$themdau2,$themdau3,$themdau4); // gộp các mảng vảo với nhau

        //////////////////////////////////////

        // loại bỏ những biến thể trùng với họ đang tồn tại và với họ ban đầu

        $kqx=array(); $j=0;

        // danh sách họ phổ biến
        $hopb = array("nguyễn","trần","lê","phạm","huỳnh","võ","phan","trương","bùi","đặng","đỗ","ngô","vũ","hồ","hoàng","dương","đinh","đoàn","lâm","mai","trịnh","đào","cao","lý","hà","lưu","lương","châu","thái","tạ","tô","phùng","vương","văn","tăng","quách","lại","hứa","thạch","từ","diệp","chu","la","đàm","tống","giang","chung","triệu","tôn","kiều","trang","hồng","đồng","danh","lư","lữ","thân","kim","mã","bạch","liêu","tiêu","bành","âu","dư","khưu","sơn","tất","nghiêm","lục","phương","quan","mạc","lai","vòng","mạch","thiều","trà","đậu","nhan","lã","trình","ninh","vi","trầm","biện","hàng","chế","ôn","nhâm","thi","doãn","khổng","phù","đường","ông","viên","tào","cù","khương","phí","kha","ngụy","nông","ngũ","du","quang","lạc","liên","nhữ","lợi","giáp","ong","tiết","ung","quảng","long","sử","chiêm","cổ","sầm","vưu","cái","lăng","quản","vy","lường","điền","lu","khuất","khúc","phó","đới","cam","chương","nguyên","ma","đổ","thang","an","tân","thới","kiên","bồ","hạ","hàn","uông","lô","diệc","lao","tiền","vĩnh","liễu","giảng","bảo","chiêu","chí","đăng","khấu","thị","thượng","hoa","đổng","thiệu","hạp","hầu","tưởng","ưng","văng","lôi","đan","sỳ","phú","ô","cát","xa","khâu","lang","thẩm","tràn","trì","tiên","hoắc","mo","hán","diêu","trác","hình","điêu","chang","lộ","đại","cung","luân","sa","phún","hỷ","sú","nhiêu","bế","mang","ngọ","ca","lầu","thôi","ừng","cáp","sẩm","yên","ao","thòng","thích","cấn","giản","lộc","mông","công","lò","mao","chắng","tằng","hoặc","vân","vu","sái","hong","ký","bàng","đái","lồ","vỏ","hùng","kỳ","phòng","nghê","thiềm","dung","đôn","chiếng","lầm","chềnh","di","phi","thổ","khiếu","san","minh","thành","tsằn","quãng","lày","tòng","sằn","hòa","làu","thông","chim","huyền","quý","giao","mè","nìm","lượng","điểu");
        foreach ($kq as $kqt) {
              if (($kqt == $str) || (in_array($kqt, $hopb))) {unset($kqt);} 

              if (isset($kqt)) {$kqx[$j] = $kqt; $j++;}
        }

        // loại bỏ các giá trị trùng lặp
        $ukq = array_unique($kqx);

return $ukq;
}

Leave a Comment