总结下拼音搜索的常见做法。
主要功能:
基于字典分词。
尽量消除歧义词避免分错。
忽略单个拼音,原业务单个拼音可以当做简拼处理。
判断一个字符串是否是拼音,’aaao’ 不认为是拼音,原因是忽略单个拼音。(可修正,在判断长度上>1去掉)
分析:
yuegaofenghei =>最短分词会忽略 e
youerjizhen=>最长会忽略er。
基于上面两种情况,进行merge,取交集合并。
便会得到正确的分词。
输入距离算法:
输入距离,不单单是一种编辑距离,也不是相似性。
例如:
糖尿病 =>与糖尿尿病
编辑距离与汉明距离都认为只差一个(汉明距离是hash化,局部敏感这里只是说明业务原理)
余弦相似,无法判断,因为这只是一个词,无法算距离。
事实上:糖尿病肾病,应该是预期提示,而 ‘糖x尿x病’ 无论x在哪都不应该排序在前。(本业务忽略这个情况,只是简单处理,建议加权差字数字为2 up n(字数)。)
算法实现原理,定义期望因子为2,即:期望前驱出现优先,后驱,加快减少。
简单根据指数,对数原理,进行log化,与指数化加权。
实现效果,满足业务需求,而且极大的方便了输入排序。
Hacknews:
热度排序(略)设因子为1.8 ,即:过一天的衰减率为100-1,若要减缓减少重力因子即可,具体需要概率算法。
代码:
package com.hm.apollo.framework.utils;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
- Created by ant_shake_tree on 16/3/28.
/
public class Utils {
private Utils cons = new Utils();
public static String replaceBlank(String str) {
String dest = "";
if (str != null) {
Pattern p = Pattern.compile("\s+|\t|\r|\n|\+|-|&|\||!|\(|\)|\{|\}|\[|\]|"|~|\|\?|\^");
Matcher m = p.matcher(str);
dest = m.replaceAll("");
}
//+ - && || ! ( ) { } [ ] ^ " ~ * ? :
return dest;
}
// public static void main(String[] args){
// System.out.println(Utils.replaceBlank("撒地方sad^fa \t +-&&|!(){}[]" " +
// "ssa ~fas*?dfdf"));
// }
/**
* 修改敏感字符编码
*
* @param value
* @return
*/
public static String queryEncode(String value) {
String re[][] = {{"\\(", "\\\\\\\\\\("},
{"\\)", "\\\\\\);"},
{"-", "\\-"}
};
for (int i = 0; i < re.length; i++) {
value = value.replaceAll(re[i][0], re[i][1]);
}
return value;
}
/**
* 验证汉字为true
*
* @param s
* @return
*/
public static boolean isLetterorDigit(String s) {
if (s.equals("") || s == null) {
return false;
}
for (int i = 0; i < s.length(); i++) {
if (!Character.isLetterOrDigit(s.charAt(i))) {
// if (!Character.isLetter(s.charAt(i))){
return false;
}
}
// Character.isJavaLetter()
return true;
}
/**
* 马晓超
* 如果前驱匹配log函数
* 后驱匹配长度差
* 乱序 指数匹配
*
* @param base
* @param str2
* @return
*/
public static double shuruDistance(String base, String str2) {
if (base.equals(str2)) return 0;
if (str2.contains(base) && str2.startsWith(base)) {
return Math.max(Math.log(Math.abs(base.length() - str2.length())), 0.5);
}
if (str2.contains(base)) {
return Math.abs(base.length() - str2.length());
}
return Math.pow(2, Math.abs(base.length() - str2.length()));
}
public static List<String> merge(List<String> list) {
List<String> cp = new ArrayList<>(list);
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
if (list.get(i).length() >= list.get(j).length()) {
if (list.get(i).contains(list.get(j))) {
cp.remove(list.get(j));
}
} else {
if (list.get(j).contains(list.get(i))) {
cp.remove(list.get(i));
}
}
}
}
return cp;
}
public static void main(String[] args) {
List<String> getp = getPinyin("aaao");
System.out.println(isPinyin("youerjiz"));
System.out.println(isPinyin("aaao"));
System.out.println(isPinyin("youerjiz"));
System.out.println(isPinyin("youerjiz"));
}
/**
* 分词,全组合分词
*/
public static List<String> fullAssembly(String sentense) {
List<String> stringArrayList = new ArrayList<>();
for (int beginIndex = 0; beginIndex < sentense.length(); beginIndex++) {
for (int endIndex = beginIndex + 1; endIndex <= sentense.length(); endIndex++) {
String kws = sentense.substring(beginIndex, endIndex);
if ((endIndex - beginIndex) == 1) continue;
if (getPinyinMap().containsKey(kws)) {
stringArrayList.add(kws);
} else {
stringArrayList.add(kws);
}
}
}
return stringArrayList;
}
/***
* 拼音分词
*/
public static List<String> getPinyin(String pinyin) {
if (!isAlpha(pinyin)) return Lists.newArrayList();
if (pinyin.length() < 2) return Lists.newArrayList();
final List<String> longp = Lists.newArrayList();
final List<String> storp = Lists.newArrayList();
int len = 0;
int longlen = 0;
for (int i = 0; i < pinyin.length(); i++) {
StringBuffer str = new StringBuffer();
int j = i;
for (; j < pinyin.length(); j++) {
if (!isAlphaChar(pinyin.charAt(j))) {
continue;
}
str.append(pinyin.charAt(j));
if (str.length() > 1 && getPinyinMap().containsKey(str.toString())) {
len += str.length();
storp.add(str.toString());
i = j;
break;
}
}
}
int index = 0;
int jiequ = 0;
//最大切分
for (int i = 0; i < pinyin.length(); i++) {
StringBuffer str = new StringBuffer();
for (int j = i; j < pinyin.length(); j++) {
if (!isAlphaChar(pinyin.charAt(j))) {
continue;
}
str.append(pinyin.charAt(j));
index++;
if (str.length() > 1 && getPinyinMap().containsKey(str.toString())) {
i = j;
jiequ = index;
if (j == pinyin.length() - 1) {
if (!"".equals(str.toString())) {
longp.add(str.toString());
longlen += str.length();
}
break;
}
}
if ((str.length() >= 5 && !getPinyinMap().containsKey(str.toString())) || (j == pinyin.length() - 1)) {
String sub=str.toString().substring(0, jiequ);
if (!"".equals(sub)) {
longp.add(sub);
longlen += sub.length();
}
index = 0;
jiequ = 0;
break;
}
}
}
if(storp.size()==0||longp.size()==0)return storp;
if (longlen == pinyin.length()) {
return longp.stream().filter(s -> StringUtils.isNotEmpty(s)).collect(Collectors.toList());
} else if (len < pinyin.length()) {
mergePinyinList(storp,longp,len,pinyin.length());
return storp.stream().filter(s -> StringUtils.isNotEmpty(s)).collect(Collectors.toList());
}
return longp.stream().filter(s -> StringUtils.isNotEmpty(s)).collect(Collectors.toList());
}
public static boolean isAlpha(String charSequence) {
if (charSequence == null) return false;
for (char c : charSequence.toCharArray()) {
if (c == '-') continue;
if (!isAlphaChar(c)) {
return false;
}
}
return true;
}
public static boolean isAlphaChar(char c) {
if (((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) return true;
else return false;
}
/**
* 判断是否是拼音
*
* @param keyworld
* @return
*/
public static boolean isPinyin(String keyworld) {
if (keyworld.length() <= 2) {
return false;
}
List<String> list = getPinyin(keyworld);
if (list.size() > 0) {
int len = 0;
for (String k : list) {
len += k.length();
}
if (len == keyworld.length()) {
return true;
}
//排除错误
if ((keyworld.length() - len) == 1 && keyworld.startsWith(list.get(0))) return true;
}
return false;
}
private static void mergePinyinList(List<String> shortPinyin,List<String> longPinyin,int lenS,int len){
if(lenS<len){
for(int i=shortPinyin.size()-1;i>=0;i--){
String shortStr=shortPinyin.get(i);
for(int j=longPinyin.size()-1;j>=0;j--) {
String s = longPinyin.get(j);
if (s.contains(shortStr)) {
lenS+=s.length()-shortStr.length();
shortPinyin.remove(shortStr);
shortPinyin.add(i,s);
longPinyin.remove(s);
}
if(lenS>=len)
break;
}
}
}
}
private static final Map<String, Integer> stringIntegerMap = Stream.of("a", "ai", "an", "ang", "ao", "ba", "bai",
"ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao", "bie", "bin", "bing", "bo", "bu", "ca",
"cai", "can", "cang", "cao", "ce", "cen", "ceng", "cha", "chai", "chan", "chang", "chao", "che", "chen",
"cheng", "chi", "chong", "chou", "chu", "chua", "chuai", "chuan", "chuang", "chui", "chun", "chuo", "ci",
"cong", "cou", "cu", "cuan", "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "dei", "den",
"deng", "di", "dia", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan", "dui", "dun", "duo",
"e", "en", "eng", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fiao", "fo", "fou", "fu", "ga", "gai",
"gan", "gang", "gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang",
"gui", "gun", "guo", "ha", "hai", "han", "hang", "hao", "he", "hei", "hen", "heng", "hong", "hou", "hu",
"hua", "huai", "huan", "huang", "hui", "hun", "huo", "ji", "jia", "jian", "jiang", "jiao", "jie", "jin",
"jing", "jiong", "jiu", "ju", "juan", "jue", "ka", "kai", "kan", "kang", "kao", "ke", "ken", "keng", "kong",
"kou", "ku", "kua", "kuai", "kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le",
"lei", "leng", "li", "lia", "lian", "liang", "liao", "lie", "lin", "ling", "liu", "lo", "long", "lou", "lu",
"luan", "lun", "luo", "lv", "lve", "ma", "mai", "man", "mang", "mao", "me", "mei", "men", "meng", "mi",
"mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na", "nai", "nan", "nang", "nao", "ne",
"nei", "nen", "neng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", "niu", "nong", "nou", "nu",
"nuan", "nun", "nuo", "nv", "nve", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng", "pi",
"pian", "piao", "pie", "pin", "ping", "po", "pou", "pu", "qi", "qia", "qian", "qiang", "qiao", "qie", "qin",
"qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re", "ren", "reng", "ri", "rong",
"rou", "ru", "rua", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sen", "seng",
"sha", "shai", "shan", "shang", "shao", "she", "shei", "shen", "sheng", "shi", "shou", "shu", "shua",
"shuai", "shuan", "shuang", "shui", "shun", "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo",
"ta", "tai", "tan", "tang", "tao", "te", "tei", "teng", "ti", "tian", "tiao", "tie", "ting", "tong", "tou",
"tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan", "wang", "wei", "wen", "weng", "wo", "wu", "xi",
"xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu", "xu", "xuan", "xue", "xun", "ya",
"yan", "yang", "yao", "ye", "yi", "yin", "ying", "yo", "yong", "you", "yu", "yuan", "yue", "yun", "za",
"zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang", "zhao", "zhe",
"zhei", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun",
"zhuo", "zi", "zong", "zou", "zu", "zuan", "zui", "zun", "zuo", "walker")
.collect(Collectors.toMap(s -> s, s -> 1));
/**
* 拼音字典
*
* @return
*/
public static Map<String, Integer> getPinyinMap() {
return stringIntegerMap;
}
}