轉載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;
}
}
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(' '));
}
}
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 ";
}
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 ";
}
- 希望
- 中華
- 人民
- 共和國
- 中華人民共和國
- 一個
- 偉大
- 國家
- 西安
- 北京
- 家庭
- 家里
- 愛國者
- 我Love你
- 學習
- 好學生
- 學生
- 愛學
- 愛學習
5.分詞測試結果:
- dictionary loading OK!
- [oooggooo]一個簡單的最大的正向匹配器:MMSegmenter segmenter on
- 中華人民共和國|是|一個|偉大|的|國家|hello|world|
- 小|紅|是|個|愛學習|的|好學生||||||
- 中華|民|de|hello|world|人民|共|
- 中華|人民|共|
- 中華人民共和國|家|
- 愛|國|
- 愛|我Love你|
- another version:
- 中華人民共和國|是|一個|偉大|的|國家|hello|world|
- 小|紅|是|個|愛學習|的|好學生|!|!|!|!|!|
- 中華|民|de|hello|world|人民|共|
- 中華|人民|共|
- 中華人民共和國|家|
- 愛|國|
- 愛|我Love你|