拼音分词拆解算法(用于判断字符串是否是纯拼音构成,并分离出所有拼音,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算法加持(自然语言处理)。
  • 补充:若读者想要获取最细粒度的拼音,不必再原有函数上改动,可以将返回的数组结果遍历,再次调用另一个切割函数(注意另一个函数是从短到长切割),随后汇总。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容