一個簡單最大正向匹配(Maximum Matching)MM中文分詞算法的實現


 轉載http://blog.csdn.net/wzb56/article/details/7914954#

1.構建詞典內存樹的TrieNode節點類:      

package cn.wzb.segmenter.mm.bean;

import java.util.HashMap;

/**
 * 構建內存詞典的Trie樹結點
 *       
 
*/
public  class TrieNode {
     /**  結點關鍵字,其值為中文詞中的一個字  */
     public  char key = '\0';
    
     /**  如果該字在詞語的末尾,則bound=true  */
     public  boolean bound =  false;
    
     /**  指向下一個結點的指針結構,用來存放當前字在詞中的下一個字的位置  */
     public HashMap<Character, TrieNode> childs =  new HashMap<Character, TrieNode>();

     public TrieNode() {}

     public TrieNode( char key) {
         this.key = key;
    }
}


2. 最大正向匹配算法(Maximum Matching)算法的實現類:segmenter類:核心方法segment();

package cn.wzb.segmenter.mm;

import java.io.IOException;

import cn.wzb.segmenter.AbstractSegmenter;
import cn.wzb.segmenter.mm.bean.TrieDictionary;
import cn.wzb.segmenter.mm.bean.TrieNode;

public  class MMSegmenter  extends AbstractSegmenter {
     public  static TrieDictionary dict =  null;
    
     static {  // 加載詞典
        String dictionaryName = "/cn/wzb/dictionary/word_dic_utf8.txt";
        dict = TrieDictionary.getInstance(dictionaryName);
    }
    
     public MMSegmenter() {
         super("一個簡單的最大的正向匹配器:MMSegmenter");
    }

     /**
     * 詞典:用Trie樹表示,每個節點都是一個TrieNode節點
     * 每個TrieNode節點中有:
     *   1.表示一個字
     *   2.以該字為前綴的所有的下一個字的HashMap<"字", 字的TrieNode>
     *   3.bound標記,該字是不是一個詞的結尾。在最大匹配中有用(Maximum Matching) 
     * 
     * 正向MM(Maximum Matching)算法的核心思想:
     *  1. 從句子中,取詞 
     *  2. 將詞添加到分詞列表中 
     *  3. 將分詞標記 "|"添加到分詞表
     * 
     * 其中的句子中的成分分為以下幾種: 
     * 1. 非分詞:如分隔符,直接跳過 
     * 2. 分詞: 分詞分為以下幾種:
     *      a. 非中文分詞:將分隔符分隔的連續的非中文字符作為一個分詞 
     *      b. 中文分詞: 
     *          i. 詞典中的詞:作為一個分詞 
     *         ii. 詞典中的詞的前綴:將每個字作為一個分詞 
     *        iii. 非詞典中的詞: 將每個字作為一個分詞
     * 
     * 該分詞的核心:對於前綴詞的划分
     
*/

     public String segment(String sentence) {
        StringBuffer segBuffer =  new StringBuffer();

        TrieNode p = dict.getRoot();
        ;
        TrieNode pChild =  null;

         int length = sentence.length();
         int segBoundIndex = -1;  // 保存上次分詞結束字符在sentence中的位置    

         for ( int i = 0; i < length; ++i) {            
             char c = sentence.charAt(i);
             if (CharacterType.isCharSeperator(c)) { //  分隔符
                
//  do nothing;
            }  else  if (CharacterType.isCharOther(c)) { //  其他語言字符                
                  do {
                    segBuffer.append(c);
                     if(++i == length){
                         break;
                    }
                    c = sentence.charAt(i);                
                } while (CharacterType.isCharOther(c));
                  if( i != length) --i;  // 還原現場             
            }  else  if (CharacterType.isCharChinese(c)) {
                pChild = p.childs.get(Character.valueOf(c));
                 if (pChild ==  null) { //  不在詞典中的中文字符
                    segBuffer.append(c);
                }  else {
                      do { //  在詞典中的詞
                        segBuffer.append(c);
                         if (p == dict.getRoot() || pChild.bound) {  //  算法的關鍵,能夠保證前綴詞,被划分。
                            segBoundIndex = i;
                        }
                         if (++i >= length) {
                             break;
                        }
                        c = sentence.charAt(i);
                        p = pChild;
                        pChild = (TrieNode) p.childs.get(Character.valueOf(c));
                    } while (pChild !=  null);
                     // 切除非詞典中詞的前綴詞
                     if (--i >= segBoundIndex) {
                        segBuffer.delete(segBuffer.length() - (i - segBoundIndex), segBuffer.length());
                    }
                     // 還原現場
                    i = segBoundIndex;
                    p = dict.getRoot();
                }
            }
            segBuffer.append('|');  // 添加分詞標記
        }

         return  new String(segBuffer);
    }

     public String segment(String sentence, String verison) {
        StringBuffer segBuffer =  new StringBuffer();

         int segBoundIdx = 0;
         int length = sentence.length();
        TrieNode p =  null
        TrieNode pChild =  null;
        
         for ( int i = 0; i < length; i++) {
             char c = sentence.charAt(i);
            
            p = dict.getRoot();            
            pChild = p.childs.get(Character.valueOf(c));
            
             //  不在詞典中的字符
             if (pChild ==  null) {
                 if (CharacterType.isCharSeperator(c)){
                    segBuffer.append(c); //  do something;
                }  if (CharacterType.isCharChinese(c)) {
                    segBuffer.append(c);
                }  else {
                     do {  //  非中文字符
                        segBuffer.append(c);
                         if (++i == length){
                             break;
                        }                        
                        c = sentence.charAt(i);
                    }  while (CharacterType.isCharOther(c));
                     if( i != length) --i;  // 還原現場
                }
            }  else {  //  中文字詞
                 while (pChild !=  null) {
                     if (p == dict.getRoot() || pChild.bound) {  // 詞典中的詞或者詞典中詞的前綴詞;前綴詞將被單字划分
                        segBoundIdx = i;
                    }
                    segBuffer.append(c);                    
                     if (++i == length) {
                         break;
                    }                                    
                    c = sentence.charAt(i);
                    p = pChild;    
                    pChild = p.childs.get(Character.valueOf(c));
                }
                 // 切除分詞表中不在詞典中的前綴字詞
                 if (--i > segBoundIdx) {
                    segBuffer.delete(segBuffer.length() - (i - segBoundIdx), segBuffer.length());
                }
                 // 還原現場
                i = segBoundIdx;                
            }            
            segBuffer.append('|');
        }
        
         return  new String(segBuffer);
    }

     public  static  void main(String args[])  throws IOException {
        MMSegmenter mmsegger =  new MMSegmenter();
        System.out.println(mmsegger.segment("中華人民共和國是一個偉大的國家hello world"));
        System.out.println(mmsegger.segment("小紅是個愛學習的好學生!!!!!"));
        System.out.println(mmsegger.segment("中華民de hello world!人民共"));
        System.out.println(mmsegger.segment("中華人民共"));
        System.out.println(mmsegger.segment("中華人民共和國家"));
        System.out.println(mmsegger.segment("愛國"));
        System.out.println(mmsegger.segment("愛我Love你"));
        System.out.println(mmsegger.segment("京華時報2008年1月23日報道 昨天,受一股來自中西伯利亞的強冷空氣影響,本市出現大風降溫天氣,白天最高氣溫只有零下7攝氏度,同時伴有6到7級的偏北風。"));
        
        System.out.println("another version: ");        
        System.out.println(mmsegger.segment("中華人民共和國是一個偉大的國家hello world", " "));
        System.out.println(mmsegger.segment("小紅是個愛學習的好學生!!!!!", " "));
        System.out.println(mmsegger.segment("中華民de hello world!人民共", " "));
        System.out.println(mmsegger.segment("中華人民共", " "));
        System.out.println(mmsegger.segment("中華人民共和國家", " "));
        System.out.println(mmsegger.segment("愛國", " "));
        System.out.println(mmsegger.segment("愛我Love你", " "));
        System.out.println(mmsegger.segment("京華時報2008年1月23日報道 昨天,受一股來自中西伯利亞的強冷空氣影響,本市出現大風降溫天氣,白天最高氣溫只有零下7攝氏度,同時伴有6到7級的偏北風。", ""));
    

        

         // System.out.println(CharacterType.isCharSeperator(' '));    
    }    
}

3.關於字符類型輔助類:CharacterType類:

package cn.wzb.segmenter.mm;

class CharacterType {    
     public  static  boolean isCharSeperator( char c) {
         return "\u3002\uFF01\uFF1F\uFF1A\uFF1B\u3001\uFF0C\uFF08\uFF09\u300A\u300B\u3010\u3011{}\u201C\u201D\u2018\u2019!?:;,()<>[]{}\"'\n\r\t ".indexOf(c) != -1;
    }
    
     public  static  boolean isCharChinese( char c) {
         return c >= '\u4E00' && c <= '\u9FBF';
    }
    
     public  static  boolean isCharOther( char c) {
         return !isCharSeperator(c) && !isCharChinese(c);
    }

     // private static final String C_E_SEPERATOR = "\u3002\uFF01\uFF1F\uFF1A\uFF1B\u3001\uFF0C\uFF08\uFF09\u300A\u300B\u3010\u3011{}\u201C\u201D\u2018\u2019!?:;,()<>[]{}\"'\n\r\t ";
    
// private static final String str = "。!?:;、,()《》【】{}“”‘’!?:;,()<>[]{}\"'\n\r\t ";
}
4. 該算法使用的詞典文件:

[java]  view plain copy
  1. 希望  
  2. 中華  
  3. 人民  
  4. 共和國  
  5. 中華人民共和國  
  6. 一個  
  7. 偉大  
  8. 國家  
  9. 西安  
  10. 北京  
  11. 家庭  
  12. 家里  
  13. 愛國者  
  14. 我Love你  
  15. 學習  
  16. 好學生  
  17. 學生  
  18. 愛學  
  19. 愛學習  

5.分詞測試結果:


[java]  view plain copy
  1. dictionary loading OK!  
  2. [oooggooo]一個簡單的最大的正向匹配器:MMSegmenter segmenter on  
  3. 中華人民共和國|是|一個|偉大|的|國家|hello|world|  
  4. 小|紅|是|個|愛學習|的|好學生||||||  
  5. 中華|民|de|hello|world|人民|共|  
  6. 中華|人民|共|  
  7. 中華人民共和國|家|  
  8. 愛|國|  
  9. 愛|我Love你|  
  10. another version:   
  11. 中華人民共和國|是|一個|偉大|的|國家|hello|world|  
  12. 小|紅|是|個|愛學習|的|好學生|!|!|!|!|!|  
  13. 中華|民|de|hello|world|人民|共|  
  14. 中華|人民|共|  
  15. 中華人民共和國|家|  
  16. 愛|國|  
  17. 愛|我Love你|  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM