中文分詞算法-百度面試題

題目:
給定一個字符串, 一個數組,判斷這個字符串能否被分隔成字典裏的一個個單詞。

用動態規劃算法
我面試時寫的是下面的代碼

public static boolean divied2(String s,String[] dict){
        boolean result=false;
        if(s.length()==0)
             return true;
        for (int i = 0; i < dict.length; i++) {
            int index=s.indexOf(dict[i]);
            if (index!=-1) {
                System.out.println(index);
                String tmp1=s.substring(0,index);
                String tmp2=s.substring(index+dict[i].length(),s.length());
                return divied(tmp1+tmp2,dict);
            }
        }
        return result;
    }

但是對於測試用例

String[]  dict={"百度一","百度","一下","我就","知","道"};
        System.out.println(divied2("百度一下我就知道", dict));

這個是不通過的。因爲百度一這個詞先刪除了,一下這個單詞被破壞了,

回來想了一下,上面的原因是終止了遍歷。經過改進後,測試通過
原題在於這個|=操作,也就是說對所有的結果進行或操作,有一個可以分隔完全就可以。


    public static boolean divied(String s,String[] dict){
        boolean result=false;
        if(s.length()==0)
             return true;
        for (int i = 0; i < dict.length; i++) {
            int index=s.indexOf(dict[i]);
            if (index!=-1) {
                System.out.println(index);
                String tmp1=s.substring(0,index);
                String tmp2=s.substring(index+dict[i].length(),s.length());
                result|=divied(tmp1+tmp2,dict);
            }
        }
        return result;
    }

缺點是時間複雜度過高,
字符串長爲m,字典大小爲n
那麼時間複雜度爲:
n^(m)左右

public static boolean divied(String s,String[] dict){
        boolean result=false;
        if(s.length()==0)
             return true;
        for (int i = 0; i < dict.length; i++) { 
            count++;
            int index=s.indexOf(dict[i]);
            if (index!=-1) {
                System.out.println(index);
                String tmp1=s.substring(0,index);
                String tmp2=s.substring(index+dict[i].length(),s.length());
                result|=divied(tmp1+tmp2,dict);
                if (result) {//優化點
                    return true;
                }
            }
        }
        return result;
    }

優化思路。在result=true的情況下直接終止循環。
加個全局變量看函數執行次數
不在中斷的情況下
函數執行了180次左右(和詞曲順序有關)。
在加了中斷之後。
函數只執行了21次。

在不斷調整詞典順序的情況下,如果字符串可以分隔完全,函數只執行了最多30次左右。但是如果不可以的話,還是會執行374。

優化思路二:
如果在字符串裏每個詞只出現了一次,可以直接在找到並刪除這個詞後將詞典裏的這個詞刪除,這樣就可以避免不必要的循環

優化思路三:
其實或操作是針對於詞典裏以同一個字符爲開頭的長度不同的詞設計的。這可以在程序裏表現的更針對一點。

改進後如下,效果非常好。無論字符串能不能被分隔完全 ,兩種情況的時間複雜度是基本一樣的。
對於如下測試用例:可以分隔,執行了44次,不可以的情況是60次
時間複雜度降爲詞典長度的階乘。

String[]  dict={"百度一","一下","知","我就","百度","道"};
        System.out.println(divied("百度一下我就知道", dict));
String[]  dict={"百度一","一下","知","我就","百度","道"};
        System.out.println(divied("百度一下我後就知道", dict));
public static boolean divied(String s,String[] dict){
        boolean result=false;
        if(s.length()==0)
             return true;
        char start='\0';
        for (int i = 0; i < dict.length; i++) {
            count++;
            int index=s.indexOf(dict[i]);
            if (start=='\0'&&index!=-1||index!=-1&&dict[i].charAt(0)==start) {
                System.out.println(index);
                String tmp1=s.substring(0,index);
                String tmp2=s.substring(index+dict[i].length(),s.length());
                start=dict[i].charAt(0);
                result|=divied(tmp1+tmp2,dict); 
                if (result) {
                    return true;
                }
            }
        }
        return result;
    }

優化思路四:
對於思路三的改進 ,只在有重複開頭的詞的情況下才進行遞歸,其它的刪除,繼續循環,不遞歸


public class Divide {

    static int count=0;
    public static boolean divied(String s,String[] dict){
        boolean result=false;
        if(s.length()==0)
             return true;
        char start='\0';
        for (int i = 0; i < dict.length; i++) {
            count++;
            int index=s.indexOf(dict[i]);
            if(start=='\0'&&index!=-1){
                String tmp1=s.substring(0,index);
                String tmp2=s.substring(index+dict[i].length(),s.length());
                s=tmp1+tmp2;
                start=dict[i].charAt(0);
            }
            if (index!=-1&&dict[i].charAt(0)==start) {
                String tmp1=s.substring(0,index);
                String tmp2=s.substring(index+dict[i].length(),s.length());
                s=tmp1+tmp2;
                result|=divied(tmp1+tmp2,dict); 
                if (result) {
                    return true;
                }
            }
        }
        return result;
    }
    public static void main(String[] args){
        String[]  dict={"百度一","百度","我就","一下","知","道"};
        System.out.println(divied("百度一下我就知道", dict));
        System.out.println(count);
    }
}

最後成果,對於可以完全分隔的,循環次數爲6
對於不可以完全分隔的,循環次數爲18

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章