ICTCLAS和别的分司系统不一样的地方就是于--N最短路径分词算法。所谓N最短路径其实就是最短路径和最大路径的折中,保留前N个最优路径。这样做的目的就是对这两种方法取长补短,既能达到一个比较理解的分词不达意效果,又能保证分词不达意速度。在此处,我们中国人的中庸思想被完美体现:)。
在N-最短路径求解之前,ICTCLAS首先通过二叉分词图表(邻接表,如下图一所示)表示出了每个词组之间的耦合关系,每一个节点都表示分词图表中的一条边,它的行值代表边的起点(前驱),它的列值代表边的终点(后驱),这一点务必弄清楚。可以通过图一、图二相结合对照来理解。通过计算词组之间的耦合关系,来最终确定初次的分词路径。我们都知道Dijkstra算法是求源点到某一点的最短路径,也就是最优的那一条,在此处的N-最短路径指的是找出前N条最优的路径(实际上在FreeICTCLAS的源代码当中N是等于1的,即nValueKind==1)。按照Dijkstra的表示方法把二叉分词图表转化成图二的表示形式,就能比较清楚地看出来,求解的过程实际就是求源点0到终于12的最短路径,和纯粹的Dijkstra算法不同的地方是在此处需要记录每个节点的N个前驱,Dijkstra当中记录一个即可。

图一

图二
在求解过程中,源程序通过二维数组m_pParent[i][j]、m_pWeight[m][n]来记录每个节点的N个前驱和每个前驱和权重,而求解最短路径权重时借用了一个队列来实现排序,数据结构如下图三所示:

图四
在源程序中,N最短路径是在CNShortPath类里里面实现的。
bool
CSegment::BiSegment(
char
*
sSentence,
double
dSmoothingPara, CDictionary
&
dictCore, CDictionary
&
dictBinary, unsigned
int
nResultCount)

...
{

......

//调用构造函数,生成一个二维链表,如下图一所示。每个链表节点是一个队列,数据结构如下图二所示
CNShortPath sp(&aBiwordsNet,nResultCount);

//最短路径算法实现
sp.ShortPath();

//输出最短路径
sp.Output(nSegRoute,false,&m_nSegmentCount);

.....

}

对源代码进行解析,以“他说的确实在理”为实例:
//
进行N-最短路径的求解,找出每一个节点的前驱计算前驱的权值(从源点到该前驱节点)
int
CNShortPath::ShortPath()

...
{
unsigned int nCurNode=1,nPreNode,i,nIndex;
ELEMENT_TYPE eWeight;
PARRAY_CHAIN pEdgeList;
//遍历所示节点,按列优先原则,从1开始
//m_apCost其实是一个邻接表,或者叫稀疏矩阵,如图一所示,
//每一个节点代表的是分词路径中的一条边,
//该节点的行值代表边的起点,该节点的列值代表该边的终点
for(;nCurNode<m_nVertex;nCurNode++)

...{
CQueue queWork;

//得到从nCurNode开始的所有结点,列优先原则
eWeight=m_apCost->GetElement(-1,nCurNode,0,&pEdgeList);//Get all the edges

//遍历列下标等于nCurNode的所有结点,即遍历邻接表中所有终点为nCurNode的边
while(pEdgeList!=0 && pEdgeList->col==nCurNode)

...{