一个简单最大正向匹配(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你|  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮你编写一个正向最大匹配分词程序。正向最大匹配分词是一种基于词典的分词方法,其思想是从左到右扫描待分词的文本,每次取最大长度的词进行匹配,直到扫描完整个文本。 以下是正向最大匹配分词程序的代码: ```python def forward_maximum_matching(text, vocab): # 设置最大匹配长度 max_len = max(len(word) for word in vocab) # 存储分词结果 seg_list = [] # 从左到右扫描文本 while text: # 取最大长度的词进行匹配 word = text[:max_len] while word not in vocab: # 如果匹配不成功,则将词长-1,直到长度为1 if len(word) == 1: break word = word[:-1] # 将匹配成功的词加入分词结果中 seg_list.append(word) # 将文本指针后移 text = text[len(word):] return seg_list ``` 在这段代码中,我们首先设置了最大匹配长度,即词典中最长词的长度。然后,我们从左到右扫描文本,每次取最大长度的词进行匹配,如果匹配不成功,则将词长-1,直到长度为1。匹配成功后,将该词加入分词结果中,并将文本指针后移。最终返回分词结果。 以下是使用示例: ```python # 读取文件 with open('CDIAL-BIAS-race/train/high/high_1.txt', 'r', encoding='utf-8') as f: text = f.read() # 进行分词 seg_list = forward_maximum_matching(text, vocab) print(seg_list) ``` 在这个代码中,我们首先读取了CDIAL-BIAS-race数据集中的一个文件,并使用上一题中抽取的词表进行了分词。最后,我们打印输出了分词结果。 希望这个程序能够帮助你完成任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值