分词目标:
在词典中进行扫描,尽可能地选择与词典中最长单词匹配的词作为目标分词,然后进行下一次匹配。
算法流程:
假设词典中最长的单词为 5 个(MAX_LENGTH),那么最大匹配的起始子串字数也为 5 个
(1)扫描字典,测试读入的子串是否在字典中
(2)如果存在,则从输入中删除掉该子串,重新按照规则取子串,重复(1)
(3)如果不存在于字典中,则从右向左减少子串长度,重复(1)
分词实例:
比如说输入 “北京大学生前来应聘”,
正向匹配法最终的切分结果为:”北京大学 / 生前 / 来 / 应聘”
正向匹配法实现代码如下:
public List leftMax(String str) {
List results = new ArrayList();
String input = str;
while( input.length() > 0 ) {
String subSeq;
// 每次取小于或者等于最大字典长度的子串进行匹配
if( input.length() < MAX_LENGTH)
subSeq = input;
else
subSeq = input.substring(0, MAX_LENGTH);
while( subSeq.length() > 0 ) {
// 如果字典中含有该子串或者子串颗粒度为1,子串匹配成功
if( dictionary.contains(subSeq) || subSeq.length() == 1) {
results.add(subSeq);
// 输入中从前向后去掉已经匹配的子串
input = input.substring(subSeq.length());
break; // 退出循环,进行下一次匹配
} else {
// 去掉匹配字段最后面的一个字
subSeq = subSeq.substring(0, subSeq.length() - 1);
}
}
}
return results;
}
分词目标:
在词典中进行扫描,尽可能地选择与词典中最长单词匹配的词作为目标分词,然后进行下一次匹配。
在实践中,逆向最大匹配算法性能优于正向最大匹配算法。
算法流程:
假设词典中最长的单词为 5 个(MAX_LENGTH),那么最大匹配的起始子串字数也为 5 个
(1)扫描字典,测试读入的子串是否在字典中
(2)如果存在,则从输入中删除掉该子串,重新按照规则取子串,重复(1)
(3)如果不存在于字典中,则从左向右减少子串长度,重复(1)
分词实例:
比如说输入 “北京大学生前来应聘”,
逆向匹配法最终的切分结果为:”北京/ 大学生/ 前来 / 应聘”
逆向匹配法实现如下:
public List<String> rightMax(String str) {
// 采用堆栈处理结果,后进先出
Stack<String> store=new Stack<String>();
List<String> results = new ArrayList<String>();
String input = str;
while( input.length() > 0 ) {
String subSeq;
// 每次取小于或者等于最大字典长度的子串进行匹配
if( input.length() < MAX_LENGTH)
subSeq = input;
else
subSeq = input.substring(input.length() - MAX_LENGTH);
while( subSeq.length() > 0 ) {
// 如果字典中含有该子串或者子串颗粒度为1,子串匹配成功
if( dictionary.contains(subSeq) || subSeq.length() == 1) {
store.add(subSeq);
// 输入中从后向前去掉已经匹配的子串
input = input.substring(0, input.length() - subSeq.length());
break;
} else {
// 去掉匹配字段最前面的一个字
subSeq = subSeq.substring(1);
}
}
}
// 输出结果
int size = store.size();
for( int i = 0; i < size; i ++) {
results.add(store.pop());
}
return results;
}
分词目标:
将正向最大匹配算法和逆向最大匹配算法进行比较,从而确定正确的分词方法。
算法流程:
分词实例:
就上例来看,
正向匹配最终切分结果为:北京大学 / 生前 / 来 / 应聘,分词数量为 4,单字数为 1
逆向匹配最终切分结果为:”北京/ 大学生/ 前来 / 应聘,分词数量为 4,单字数为 0
逆向匹配单字数少,因此返回逆向匹配的结果。
双向最大匹配法实现如下:
public List segment() {
List fmm = this.leftMax();
List bmm = this.rightMax();
// 如果分词的结果不同,返回长度较小的
if( fmm.size() != bmm.size()) {
if ( fmm.size() > bmm.size())
return bmm;
else
return bmm;
}
// 如果分词的词数相同
else {
int fmmSingle = 0, bmmSingle = 0;
boolean isEqual = true;
for( int i = 0; i < bmm.size(); i ++) {
if( !fmm.get(i).equals(bmm.get(i))) {
isEqual = false;
}
if( fmm.get(i).length() == 1)
fmmSingle ++;
if( bmm.get(i).length() == 1)
bmmSingle ++;
}
// 如果正向、逆向匹配结果完全相等,返回任意结果
if ( isEqual ) {
return fmm;
// 否则,返回单字数少的匹配方式
} else if ( fmmSingle > bmmSingle)
return bmm;
else
return fmm;
}
}
这里的字典文件采用的是
http://download.csdn.net/download/yuanlulu/2380141
载入字典和自定义添加词实现如下:
private static Set dictionary;
// 初始化字典,采用 hashset 存储
public void getDictionary() {
dictionary = new HashSet();
String dicpath = "data/worddict2.txt";
String line = null;
BufferedReader br;
try {
// 按照 gbk 编码读入文件
br = new BufferedReader(new InputStreamReader(new FileInputStream(dicpath),"gbk"));
try {
while(((line = br.readLine())!=null)) {
// 按照空格切分,只读取第二部分
String[] str = line.split("\\s+");
line = str[1];
dictionary.add(line);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (UnsupportedEncodingException | FileNotFoundException e) {
e.printStackTrace();
}
}
// 自定义添加词汇
public void addWord(String str) {
dictionary.add(str);
}
可以看到效果还不错,最大匹配法的效果还是取决于字典的质量。
整体代码如下:
package mm;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
public class MMSegment {
private String request;
private int MAX_LENGTH = 5;
private static Set dictionary;
public void getDictionary() {
dictionary = new HashSet();
String dicpath = "data/worddict2.txt";
String line = null;
BufferedReader br;
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(dicpath),"gbk"));
try {
while(((line = br.readLine())!=null)) {
String[] str = line.split("\\s+");
line = str[1];
dictionary.add(line);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (UnsupportedEncodingException | FileNotFoundException e) {
e.printStackTrace();
}
}
public void addWord(String str) {
dictionary.add(str);
}
public List leftMax() {
List results = new ArrayList();
String input = request;
while( input.length() > 0 ) {
String subSeq;
if( input.length() < MAX_LENGTH)
subSeq = input;
else
subSeq = input.substring(0, MAX_LENGTH);
while( subSeq.length() > 0 ) {
if( dictionary.contains(subSeq) || subSeq.length() == 1) {
results.add(subSeq);
input = input.substring(subSeq.length());
break;
} else {
subSeq = subSeq.substring(0, subSeq.length() - 1);
}
}
}
return results;
}
public List rightMax() {
Stack store=new Stack();
List results = new ArrayList();
String input = request;
while( input.length() > 0 ) {
String subSeq;
if( input.length() < MAX_LENGTH)
subSeq = input;
else
subSeq = input.substring(input.length() - MAX_LENGTH);
while( subSeq.length() > 0 ) {
if( dictionary.contains(subSeq) || subSeq.length() == 1) {
store.add(subSeq);
input = input.substring(0, input.length() - subSeq.length());
break;
} else {
subSeq = subSeq.substring(1);
}
}
}
int size = store.size();
for( int i = 0; i < size; i ++) {
results.add(store.pop());
}
return results;
}
public List segment() {
List fmm = this.leftMax();
List bmm = this.rightMax();
if( fmm.size() != bmm.size()) {
if ( fmm.size() > bmm.size())
return bmm;
else
return fmm;
}
else {
int fmmSingle = 0, bmmSingle = 0;
boolean isEqual = true;
for( int i = 0; i < bmm.size(); i ++) {
if( !fmm.get(i).equals(bmm.get(i))) {
isEqual = false;
}
if( fmm.get(i).length() == 1)
fmmSingle ++;
if( bmm.get(i).length() == 1)
bmmSingle ++;
}
if ( isEqual ) {
return fmm;
} else if ( fmmSingle > bmmSingle)
return bmm;
else
return fmm;
}
}
public void test(String str) {
request = str;
System.out.println(this.segment());
}
public static void main(String[] args) {
MMSegment f = new MMSegment();
f.getDictionary();
f.test("研究生命科学");
f.test("研究生命令本科生");
f.test("我从马上下来");
f.test("北京大学生喝进口红酒");
f.test("美军中将竟公然说");
f.test("阿美首脑会议将讨论巴以和平等问题");
f.addWord("巴以和平");
System.out.println("---------------------------");
System.out.println("向字典中添加'巴以和平'后");
f.test("阿美首脑会议将讨论巴以和平等问题");
f.test("我不想吃东西");
}
}
[1] http://blog.csdn.net/worldwindjp/article/details/18085725
[2] http://blog.csdn.net/hu948162999/article/details/43608107
[3] http://blog.csdn.net/xiaoyeyopulei/article/details/25194021
[4] http://blog.csdn.net/chenlei0630/article/details/40710441