系列アライメント

アルゴリズムデザインにのっていたので、c++で実装してみた。
系列アライメントとは、Googleの「もしかして:」の機能のような似ている文字列を探すやつのこと。
二つの文字列で動的計画法を使って類似度を計算する。

入力

単語(辞書)
...
#
チェックする単語
...

実装

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int INF = 1 << 29;
const int MAX_WORD_N = 100;	//辞書の単語数
const int MAX_WORD_LEN = 20;	//単語の最大文字数
const int GAP_PENALTY = 3;	//ギャップペナルティ
int MISMATCH_COST[128][128];	//ミスマッチコスト
char DIC[MAX_WORD_N][MAX_WORD_LEN + 1];//辞書

int read_dic();		//辞書読み込み関数
int compare(const char *, const char *);//類似度計算関数

int main(){
	int word_n;
	char s[MAX_WORD_LEN + 1];
	word_n = read_dic();	//辞書読み込み
	printf("INCLUDED DICTIONARY\nword_n=%d\n",word_n);
	while(scanf("%s", s) != EOF){

	int similarity = INF;	//類似度 小さいほど類似
	int p = -1;		//もっとも類似している単語の番号
	for(int i = 0; i < word_n; i++){
		//類似度計算
		int t = compare(s, &DIC[i][0]);
		if(t < similarity){
			//もっとも類似している単語を記憶
			similarity = t;
			p = i;
		}
	}
	printf("%s\n", s);
	printf("もしかして:%s\n類似度%d\n\n", &DIC[p][0], similarity);
	}
	return 0;
}

int compare(const char *s1, const char *s2){
	int dp[MAX_WORD_LEN + 1][MAX_WORD_LEN + 1];
	int s1_len, s2_len;
	s1_len = strlen(s1); s2_len = strlen(s2);
	dp[0][0] = 0;
	for(int i = 1; i <= s1_len; i++)
		dp[i][0] = dp[i - 1][0] + GAP_PENALTY;
	for(int j = 1; j <= s2_len; j++)
		dp[0][j] = dp[0][j - 1] + GAP_PENALTY;
	for(int i = 1; i <= s1_len; i++){
		for(int j = 1; j <= s2_len; j++){
			dp[i][j] = min(min(dp[i-1][j], dp[i][j-1])+GAP_PENALTY, dp[i-1][j-1]+MISMATCH_COST[s1[i]][s2[j]]);
		}
	}
	return dp[s1_len][s2_len];
}
int read_dic(){
	int n = 0;
	char c;
	char s[MAX_WORD_LEN];
	while(scanf("%s", &DIC[n][0]) != EOF){
		scanf("%c", &c);
		if(DIC[n][0] == '#') break;
		n++;
	}
	for(int i = 0; i < 128; i++){
		for(int j = 0; j < 128; j++){
			if(i == j)	MISMATCH_COST[i][j] = 0;
			else		MISMATCH_COST[i][j] = 4;
		}
	}
	return n;
}