代码改变世界

@功能的猜测

2012-08-17 00:19  ggzwtj  阅读(203)  评论(0编辑  收藏  举报

闲来无事,八卦一下@功能(下面的规则是用新浪微博做的实验)。

先举个例子:如果有一个昵称是【明天hui更好】,由于支持拼音,需要匹配下面的两个串:

  • 【明,天,h,u,i,更,好】;
  • 【ming,tian,h,u,i,geng,hao】;

通过测试发现匹配的规则还是比较苛刻的:

  • 如果是汉字匹配汉字,必须完全匹配,比如【名】和【明】无法匹配;
  • 如果是拼音匹配汉字,必须和汉字的读音完全匹配【min】和【ming】无法匹配(除非是【min天】这种后面是汉字的);
  • 必须从头匹配到尾,【明天更好】和【明天hui更好】是无法匹配的;

还有一个放宽了的地方是:

  • 【更好】是可以和【明天hui更好】匹配的; 

如果按照上的功能做@的话就很简单了:

  1. 把用户关注的人的昵称取回来:别如【明天hui更好】等;
  2. 依次判断用户的输入是否和关注人的昵称匹配:
    1. 根据关注人的昵称生成对应的拼音数组【ming,tian,h,u,i,geng,hao】;
    2. 根据用户输入的第一个字符找到开始匹配的下标:
      1. 判断从该位置能否完全匹配,如果能就加入结果列表;
        1. X:正在匹配的输入的下标,Y:昵称的下标;
        2. 如果X处是汉字,且匹配,X++,Y++;否则,找下一个第一个匹配的Y的位置;
        3. 如果X处是汉字,且匹配,则继续匹配整个的拼音(或者是到下个汉字前的拼音),比如X为【m】,Y为【明】,那么就要匹配到【ming】,然后X+=4,Y++;
        4. X为输入的长度时匹配完成;   
      2. 根据距离判断能否再次匹配;

最后要想一下如何将这些结果排序:

  • 首字匹配的一定比从中间才开始匹配的准确;
  • 经常联系的同一种经常联系的放在前面?

最后再看效率: 

在一般的情况下,昵称中相同的字符数量不是会很多,在最极端的情况用户输入的是输入的是长度为N的【qqq..p】,而关注的人的昵称是长度为M的【qqqqq...】,这种情况下要比较的次数为NxM,在关注少于2000的时候也毫无压力。

如果一定认为昵称中重复的字符的数目非常非常多,上KMP和自动机都是没有问题的,但是,可能大部分的时间消耗在了这些上面,有时候复杂的东西不一定是高效的,尤其是为了一些小概率事件。实际的数据不会像程序比赛的时候那样拿变态的数据故意卡你。

最后可以放开【连续字符匹配】和【拼音完全匹配】,这个时候就有点意思的,不过没有什么实际意义。。。

如果想@的时候在所有的人里面找,那估计PD的脑袋是被驴踢了。

 

public class AntiUtil {
    /**
     * [input]->[word] [word] [word] [word]
     * 
     * [nick]-->[word] [word] [word] [word]
     *          [pin1] [pin1] [pin1] [pin1]
     *          [pin2] [pin2] [pin2]
     *          [pin3]        [pin3]
     */
    public static boolean nickMatch(String nick, String input){
        if(input == null || input.length() <= 0){
            return false;
        }
        
        int nickLength = nick.length(), x = 0, y = 0, inputLength = input.length();
        String[][] pinyinArray = new String[nickLength][];
        
        for(int i = 0; i < nickLength; i++){
            pinyinArray[i] = PinyinHelper.toHanyuPinyinStringArray(nick.charAt(i));
        }
        nextStartChar:
        for(int i = 0; i < nickLength; i++){
            //System.out.println("start:" + nick.charAt(i+x));
            nextInputChar:
            while(y < inputLength){
                if(i+x >= nickLength){
                    x = 0;
                    y = 0;
                    continue nextStartChar;
                }
                // [x+i] vs [y]
                // input is chiness || nick is english.(3 situation).
                if(!isEnglish(input.charAt(y)) || isEnglish(nick.charAt(x+i))){
                    if(nick.charAt(i+x) != input.charAt(y)){
                        x = y = 0;
                        continue nextStartChar;
                    }else{
                        x++;
                        y++;
                    }
                    continue;
                }
                // input is english || nick is chiness.(1 situation).
                int oldY = y;
                nextPinYin:
                for(int j = 0; pinyinArray[i+x] != null && j < pinyinArray[i+x].length; j++){
                    y = oldY;                    
                    for(int k = 0; k < pinyinArray[i+x][j].length() && y < inputLength; k++){
                        if(k == pinyinArray[i+x][j].length()-1){
                            x++;
                            continue nextInputChar;
                        }
                        if(pinyinArray[i+x][j].charAt(k) != input.charAt(y)){
                            // success.
                            if(y > 0){
                                x++;
                                continue nextInputChar;
                            }else{// fail.
                                continue nextPinYin;
                            }
                        }else{
                            y++;
                        }                    
                    }
                }
                if(y == inputLength){
                    return true;
                }
                // not find pinyin.
                x = 0;
                y = 0;
                continue nextStartChar;
            }
            // success
            if(y == inputLength){
                return true;
            }
        }
        return false;
    }
    public static List<String> getNicksByInput(List<String> nickList, String input, int maxCount){
        input = input.toLowerCase();
        LinkedList<String> result = new LinkedList<String>();
        for(String nick : nickList){            
            try{
                if(nickMatch(nick, input)){
                    result.addLast(nick);
                    if(maxCount-- <= 0){
                        return result;
                    }
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        return result;
    }
    public static boolean isEnglish(char ch){
        return (ch >='A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
    }
    /***************************TEST***************************/
    public static void main(String[] args){        
        System.out.println(nickMatch("单干一支花", "dgyzh"));
        System.out.println(nickMatch("单干一支花", "sg"));
        System.out.println(nickMatch("单干一支花", "一支花"));
        System.out.println(nickMatch("单干一支花", "花"));
        System.out.println(nickMatch("单干一支花", "gan一支"));
        System.out.println(nickMatch("单干一支花", "shgb"));
    }
}

------------------------------------------

欢迎拍砖。