文本或模式串中的拼写错误让我们不得不找出“相似”而非相等的匹配;基因序列或语言文字的演化让我们
常常用古老的模式在脑海里搜索:“尔等休要取人性命”变成了“你们别杀人”。
如果我们要处理非精确匹配,首先就应该定义一个代价函数,衡量两个字符串“差得有多远”,即两个字符串之间的距离度量。
一个合理的距离度量是把一个字符串变到另一个字符串所需的最小修改次数。有3种常见的修改方式:
·替换-把模式串s中的一个字符替换成文本串t中的一个不同字符,例如把“shot”变成“spot”。
·插入-在模式串s中插入一个新字符,使得它和t更接近。例如把”ago“变成”agog“。
·删除-在模式串s中删除一个字符,使得它和t更接近。例如把”hour“变成”our“
给出一个具体的字符串相似度定义需要为上述三种变换操作分别设置代价。如果每个操作的代价均设为1,则相应的度量称为编辑距离(edit distance)。
代码如下:
#define MATCH 0/*enumerated type symbol for match*/ #define INSERT 1/*enumerated type symbol for insert*/ #define DELETE 2/*enumerated type symbol for delete*/ #define MAXLEN 99 #include<stdio.h> #include<string.h> typedef struct{ int cost; int parent; }cell; cell m[MAXLEN+1][MAXLEN+1]; void row_init(int i) { m[0][i].cost = i; if(i > 0) m[0][i].parent = INSERT; else m[0][i].parent = -1; } void column_init(int i) { m[i][0].cost = i; if(i > 0) m[i][0].parent = DELETE; else m[0][i].parent = -1; } int match(char c,char d){ if(c == d) return 0; else return 1; } int indel(char c) { return 1; } void insert_out(char *t,int j){ printf("I"); } void delete_out(char *s,int i){ printf("D"); } void match_out(char *s,char *t,int i,int j) { if(s[i] == t[j]) printf("M"); else printf("S"); } void goal_cell(char *s,char *t,int *i,int *j){ *i = strlen(s) - 1; *j = strlen(t) - 1; } void reconstruct_path(char *s,char *t,int i,int j){ if(m[i][j].parent == -1) return; if(m[i][j].parent == MATCH){ reconstruct_path(s,t,i-1,j-1); match_out(s,t,i,j); return; } if(m[i][j].parent == INSERT){ reconstruct_path(s,t,i,j-1); insert_out(t,j); return; } if(m[i][j].parent == DELETE){ reconstruct_path(s,t,i-1,j); delete_out(s,i); return; } } int string_compare(char *s,char *t) { int i,j,k; /*counter*/ int opt[3]; /*cost of the three options*/ for(i = 0; i < MAXLEN; i++){ row_init(i); column_init(i); } for(i = 1; i < strlen(s); i++) for(j = 1; j < strlen(t); j++){ opt[MATCH] = m[i-1][j-1].cost + match(s[i],t[j]); opt[INSERT] = m[i][j-1].cost + indel(t[j]); opt[DELETE] = m[i-1][j].cost + indel(s[i]); m[i][j].cost = opt[MATCH]; m[i][j].parent= MATCH; for(k = INSERT; k <= DELETE; k++) if(opt[k] < m[i][j].cost){ m[i][j].cost = opt[k]; m[i][j].parent = k; } } goal_cell(s,t,&i,&j); reconstruct_path(s,t,i,j); return(m[i][j].cost); } int main(){ char s[100],t[100]; s[0] = t[0] = ' '; scanf("%s",&(s[1])); scanf("%s",&(t[1])); int i = strlen(s); int j = strlen(t); string_compare(s,t); for(int x = 0; x < i; x++){ for(int y = 0; y < j; y++) { if(m[x][y].cost < 10) printf(" %d",m[x][y].cost); else printf(" %d",m[x][y].cost); } printf("\n"); } }
对于特定的实例,给出的是最小的操作代价值5:
这是输出代价的枚举列表:
定义D为删除操作,S为变换操作,M为保持操作,I为插入操作,下方是最优解: