您的位置:首页 > 科技 > 能源 > 拼音分词拆解算法(用于判断字符串是否是纯拼音构成,并分离出所有拼音,50行纯原生代码实现)

拼音分词拆解算法(用于判断字符串是否是纯拼音构成,并分离出所有拼音,50行纯原生代码实现)

2024/10/14 20:56:38 来源:https://blog.csdn.net/weixin_42100387/article/details/140962206  浏览:    关键词:拼音分词拆解算法(用于判断字符串是否是纯拼音构成,并分离出所有拼音,50行纯原生代码实现)

废话哔哔

都说算法是程序的灵魂,算法源于数学,数学是描述宇宙万物的语言,这话一点不假,开发出身算法用的较少,回过头看算法,用到了,递归、循环、分支、分治、合并、取舍调优的思想,确实精彩,烧脑还挺有意思。
好的技术博客必须有做到有No BB,Show Code的干货,也得有说明辅助理解,因此写了这篇博客。

需求

  • 概括:将一个字符字符串,拆分单个的拼音,例如bianchengyuyan(编程语言),拆分成bian、cheng、yu、yan。如果是zhang1san(张1三),zhangssan(张s三),则返回空数组。
  • 应用场景:这算法并非百无一用,拼音分词,词义分析,品相判别等场景能够用到。

成品代码

<?php
/*** @function 返回拼音组合,由于冗长,单独拆分出来,拼音手动总结,可能存在遗漏* @return array*/
function pinYinArr() {return [1 => ['a', 'e', 'm', 'n', 'o'],2 => ['ai','an','ao','ba','bi','bo','bu','ca','ce','ci','cu','da','de','di','du','en','er','fa','fo','fu','ga','ge','gu','ha','he','hm','hu','ji','ju','ka','ke','ku','la','le','li','lo','lu','lv','ma','me','mi','mo','mu','na','ne','ng','ni','nu','nv','ou','pa','pi','po','pu','qi','qu','re','ri','ru','sa','se','si','su','ta','te','ti','tu','wa','wo','wu','xi','xu','ya','ye','yi','yo','yu','za','ze','zi','zu'],3 => ['ang','bai','ban','bao','bei','ben','bie','bin','cai','can','cao','cen','cha','che','chi','chu','cou','cui','cun','cuo','dai','dan','dao','dei','den','dia','die','diu','dou','dui','dun','duo','eng','fan','fei','fen','fou','gai','gan','gao','gei','gen','gou','gua','gui','gun','guo','hai','han','hao','hei','hen','hng','hou','hua','hui','hun','huo','jia','jie','jin','jiu','jue','jun','kai','kan','kao','kei','ken','kou','kua','kui','kun','kuo','lai','lan','lao','lei','lia','lie','lin','liu','lou','lue','lun','luo','mai','man','mao','mei','men','mie','min','miu','mou','nai','nan','nao','nei','nen','nie','nin','niu','nou','nue','nuo','pai','pan','pao','pei','pen','pie','pin','pou','qia','qie','qin','qiu','que','qun','ran','rao','ren','rou','rua','rui','run','ruo','sai','san','sao','sen','sha','she','shi','shu','sou','sui','sun','suo','tai','tan','tao','tie','tou','tui','tun','tuo','wai','wan','wei','wen','xia','xie','xin','xiu','xue','xun','yan','yao','yin','you','yue','yun','zai','zan','zao','zei','zen','zha','zhe','zhi','zhu','zou','zui','zun','zuo'],4 => ['bang','beng','bian','biao','bing','cang','ceng','chai','chan','chao','chen','chou','chua','chui','chun','chuo','cong','cuan','dang','deng','dian','diao','ding','dong','duan','fang','feng','gang','geng','gong','guai','guan','hang','heng','hong','huai','huan','jian','jiao','jing','juan','kang','keng','kong','kuai','kuan','lang','leng','lian','liao','ling','long','luan','mang','meng','mian','miao','ming','nang','neng','nian','niao','ning','nong','nuan','pang','peng','pian','piao','ping','qian','qiao','qing','quan','rang','reng','rong','ruan','sang','seng','shai','shan','shao','shei','shen','shou','shua','shui','shun','shuo','song','suan','tang','teng','tian','tiao','ting','tong','tuan','wang','weng','xian','xiao','xing','xuan','yang','ying','yong','yuan','zang','zeng','zhai','zhan','zhao','zhei','zhen','zhou','zhua','zhui','zhun','zhuo','zong','zuan'],5 => ['chang','cheng','chong','chuai','chuan','guang','huang','jiang','jiong','kuang','liang','niang','qiang','qiong','shang','sheng','shuai','shuan','xiang','xiong','zhang','zheng','zhong','zhuai','zhuan'],6 => ['chuang', 'shuang', 'zhuang'],];
}/*** @function 移除参数1中右边包含的参数2,并返回剩余的字符,例如strRemoveRightOnce('wahaha', 'ha'),返回waha* @param    $string string 被操作字符串* @param    $part   string 要被移除的字符串* @return   string*/
function strRemoveRightOnce($str, $part) {if (substr($str, -strlen($part)) == $part) {return substr($str, 0, - strlen($part));}return $str;
}/*** @function 获取字符串存在的拼音数量,不兼容-符号,从长往短了截取* @param    $str    string 字符* @param    $result array  函数返回的结果* @return   array*/
function pinYinCutLongToShort($str, $result = []) {if($str == '') {return $result;}if(($str == '') && ($result == [])) {return [];}//判断是否是纯拼音,不是直接过滤if((! preg_match('/^[a-z]+$/', $str)) && ($result == [])) {return [];}$initial_arr = pinYinArr();$initial_keys = array_keys($initial_arr);$max = max($initial_keys);$min = min($initial_keys);for($i = $max; $i >= $min; $i--) {$substring = substr($str,  - $i);if(in_array($substring, $initial_arr[$i])) {array_unshift($result, $substring);//避免xiao ha ha,用ltrim函数,一次性移除掉了两个ha造成的计量有误return pinYinCutLongToShort(strRemoveRightOnce($str, $substring), $result);} else {if($i == $min) {return [];}}}return $result;
}print_r(pinYinCutLongToShort('bianchengyuyan'));
Array
([0] => bian[1] => cheng[2] => yu[3] => yan
)

算法调优注意的地方

  • 如果检测到含有非拼音的字符(例如有数字),以及多余的拼音字符(例如zhangxsan张x三),会直接返回空数组。
  • 代码采用从长往短切割的策略,以xianggang(香港)举例:
    • 从长到短(for循环递减):分成xiang、gang。粒度大,但是失败率小。
    • 从短到长(for循环递增):拆分成xi、an,后面的gg没法切了。粒度更小,容易出错。
    • 从长到短缺点也很明显:相应的会忽略精度,所以xian(西安)会记作一个拼音,当做xian(贤)处理。
  • 代码采用的从字符串右边往左切的策略,进一步避免切割出错。例如xianguang(闲逛):
    • 从左到右从长到短:xiang、uang没办法切了。
    • 从左到右从短到长:xi、an、gu、an、g没办法切了。
    • 从右到左从长到短:guang、xian,刚刚好。
    • 从右到左从短到长:ang、gu、an、xi,也行。
  • 结语:从长往短了切割用于减少失败率,从右往左切割,用于进一步避免出错,因此被采用。
  • 注意:以上算法,并非适合所有场景,可能存在误差,毕竟没有NLP的AI算法加持(自然语言处理)。
  • 补充:若读者想要获取最细粒度的拼音,不必再原有函数上改动,可以将返回的数组结果遍历,再次调用另一个切割函数(注意另一个函数是从短到长切割),随后汇总。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com