最近想试着自己实现一些搜索算法,受到http://www.52nlp.cn/maximum-matching-method-of-chinese-word-segmentation这位大湿的一点启发,准备用JAVA把算法实现了一遍,代码写的有些简单,请各位大神指正,
正向最大匹配法算法思想如下所示:
(注:以上最大匹配算法图来自于詹老师讲义)逆向匹配法思想与正向一样,只是从右向左切分,这里举一个例子:
输入例句:S1=”计算语言学课程有意思” ;
定义:最大词长MaxLen = 5;S2= ” “;分隔符 = “/”;
假设存在词表:…,计算语言学,课程,意思,…;
最大逆向匹配分词算法过程如下:
(1)S2=”";S1不为空,从S1右边取出候选子串W=”课程有意思”;
(2)查词表,W不在词表中,将W最左边一个字去掉,得到W=”程有意思”;
(3)查词表,W不在词表中,将W最左边一个字去掉,得到W=”有意思”;
(4)查词表,W不在词表中,将W最左边一个字去掉,得到W=”意思”
(5)查词表,“意思”在词表中,将W加入到S2中,S2=” 意思/”,并将W从S1中去掉,此时S1=”计算语言学课程有”;
(6)S1不为空,于是从S1左边取出候选子串W=”言学课程有”;
(7)查词表,W不在词表中,将W最左边一个字去掉,得到W=”学课程有”;
(8)查词表,W不在词表中,将W最左边一个字去掉,得到W=”课程有”;
(9)查词表,W不在词表中,将W最左边一个字去掉,得到W=”程有”;
(10)查词表,W不在词表中,将W最左边一个字去掉,得到W=”有”,这W是单字,将W加入到S2中,S2=“ /有 /意思”,并将W从S1中去掉,此时S1=”计算语言学课程”;
(11)S1不为空,于是从S1左边取出候选子串W=”语言学课程”;
(12)查词表,W不在词表中,将W最左边一个字去掉,得到W=”言学课程”;
(13)查词表,W不在词表中,将W最左边一个字去掉,得到W=”学课程”;
(14)查词表,W不在词表中,将W最左边一个字去掉,得到W=”课程”;
(15)查词表,“意思”在词表中,将W加入到S2中,S2=“ 课程/ 有/ 意思/”,并将W从S1中去掉,此时S1=”计算语言学”;
(16)S1不为空,于是从S1左边取出候选子串W=”计算语言学”;
(17)查词表,“计算语言学”在词表中,将W加入到S2中,S2=“计算语言学/ 课程/ 有/ 意思/”,并将W从S1中去掉,此时S1=”";
(18)S1为空,输出S2作为分词结果,分词过程结束。
第一步,加载词库,词库大家可以随便找一些分词,可以去搜狗下载他的分词,自己整理一下就可以了,每一行一个词或者"#"分割;加载词库代码如下Dictionary类如下:
package com.seg.seg;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
public class Dictionary {
private static Dictionary instance;
/**
* 单例实现此类
* @return Dictionary
*/
public static Dictionary getInstance() {
if (instance == null) {
instance = new Dictionary();
return instance;
}
return instance;
}
private static Map<String, Integer> dic = new HashMap<String, Integer>(
160000);
public boolean match(String word) {
if (dic.containsKey(word)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
/**
* 构造函数初始化词库
*/
public Dictionary() {
InputStream is = this.getClass().getClassLoader()
.getResourceAsStream("main2012.dic");
if (is != null) {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(
is, "UTF-8"));
String theWord = null;
do {
theWord = br.readLine();
if (theWord != null && !theWord.trim().isEmpty()
&& theWord.trim().charAt(0) != '#') {
dic.put(theWord.trim().toLowerCase().toString(), 1);
// System.out.println(theWord.trim().toLowerCase().toCharArray());
}
} while (theWord != null);
} catch (IOException ioe) {
System.err.println(" Dictionary loading exception。ClassName: ");
ioe.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
is = null;
System.out.print("词库加载完成");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
第二步,加载完词库后,就可以实现分词算法了,代码如下:
package com.seg.seg;
import java.util.ArrayList;
import java.util.List;
public class Segmentation {
/**
* 测试结果
* @param args
*/
public static void main(String[] args) {
Segmentation s=new Segmentation();
System.out.print(s.getSegWords("又一个能和大家无障碍沟通的渠道,想聊天的都加起来啊"));
}
/**
* 分词主方法
* @param words
* @return
*/
List<String> getSegWords(String words) {
int length=0;
String nowWords="";
List <String>resultWord = new ArrayList<String>();
while((length=words.length()) !=0) {
int maxl=5;
if(maxl >length){
maxl=length;
}
nowWords = words.substring(length -maxl, length);
while(!Dictionary.getInstance().match(nowWords)){
if(maxl ==1){
break;
}
maxl--;
nowWords = words.substring(length -maxl, length);
}
words=words.substring(0,length -maxl);
resultWord.add(nowWords);
}
return resultWord;
}
}
词库加载完成[来啊, 起, 加, 都, 的, 聊天, 想, ,, 渠道, 的, 沟通, 无障碍, 大家, 和, 能, 又一个]
只是做了简单的实现,标点符号都没有过滤,有时间我再慢慢加载内容