最近在使用java模拟一个中文拼音输入法,之所以说模拟是因为此输入法只能够在特定的文本框中输入(用java编写嘛)。为了要能够实现连续拼音序列的识别,我们使用HMM作为模型,而大名鼎鼎的Viterbi算法也是动态规划的经典应用,此算法名气之大已无需我来解释,网络上自有高人。为了能够找到构建HMM模型的观察序列(建模的第一步),对于用户输入的连续的字母序列,我们需要得到对这个序列用户最可能输入的音节。
最先想到的解决方案,当然是正向最长匹配。方法足够地简单,没有难度,但也有非常大的缺陷。比如,用户输入xianguo,按照正向最大匹配算法,xiang是一个合法的拼音,xiangu不是,所以,我们在xiang后面加入分割。然而剩下的uo却不好分了,因为,uo或者u'o都是不合法的拼音。
那么,分出什么样的音节,对于一个输入的字母序列来说,是一个好的分隔呢?
首先,分出的音节应当尽量地少。对于输入danteng,当然,可以分成d'a'n't'e'n'g,这样分是没有问题的,不过确实很蛋疼。用户最有可能的意图应该是dan'teng。
其次,分出的音节应该尽量地完整。如何定义完整呢?我们对于一个可能的音节,分为3种状态,我们定义costs函数。当这个音节Py是一个完整的合法音节时,costs(Py) = 0;当这个音节Py不是一个完整的合法音节,但是它是至少一个合法音节的前缀时,costs(Py) = 1;当它连合法音节的前缀都不可能是的时候,我们定义costs(Py) = 2.我们用一个音节的costs的值作为对其完整性的判断,显然,costs值越低越好。比如,对于输入:gonga,对于第一点要求,分割gong'a与gon'ga都符合要求。但对于第二个要求,gon不是一个完整的合法拼音,它只是一个合法完整拼音的前缀,而在第一个分割中,两个都是完整合法的拼音,所以,这里选择前者作为最优分割。
这样,我们形式化定义问题为:
输入:一个可能含有多个音节的拼音序列
输出:满足上述条件(音节最少,音节最完整)的分隔好的音节