#include <stdio.h> #include <stdlib.h> #include <string.h> /* * P = c m p x l e e r | | | * T = c o m p i l e r * P与T是3近似匹配,不同与缺失共三处 * 为了计算,可以生成一张模式P为行,查找文件T为列的表 * 对于p[1...n]与T[j]结尾的子串的最少差别可以由以下规则计算 * 1. D[i-1, j-1] + diff(P[i], T[j]) 若 P[i]==T[j] diff(P[i], T[j])=0 否则 diff(P[i], T[j])=1 * 2. D[i-1, j] + 1 模式缺失即上述o的情形 * 3. D[i,j-1] + 1 文本缺失即上述e的情形 * 同时需要一个数组,根据当前计算的值,计算与原串的偏移,由于差别值的计算只与前一列有关,因此 * 只需要两列就可以完成所有计算而不需要保存一张表 * * 此算法可以不单单用于字符串的近似匹配 * * 此算法可以通过改变D值的计算表达式来改进算法,例如 * D[i-2, j-2]+diff(p[i-1], t[j])+diff(p[i], t[j-1])+1 ,用于识别相邻的字符串 * 对不同的差别可以得用权值使得更容易识别具有某种倾向的输入,例如由于简单的键盘事故失配 */ static char *Text, *Pattern; // 指向查找字符串 static int Textloc; // 当前Text的查找位置 static int Plen; // 模式的长度 static int Degree; // 允许最大不匹配度 static int *Ldiff, *Rdiff; // 动态不一致数组 static int *Loff, *Roff; // 额头于计算匹配的开始位置 void AppInit( char *pattern, char *text, int degree ) { int i; // 保存参数 Text = text; Pattern = pattern; Degree = degree; // 初始化 Plen = strlen( pattern ); Ldiff = (int *)malloc( sizeof(int)*(Plen+1)*4 ); // 一次性申请Ldiff, Rdiff, Loff, Roff, 节省分配时间 Rdiff = Ldiff+Plen+1; Loff = Rdiff+Plen+1; Roff = Loff+Plen+1; for (i=0; i<=Plen; i++) { Rdiff[i] = i; // 右边列的初始值 Roff[i] = 1; } Textloc = -1; // 当前Text偏移值 } void AppNext( char **start, char **end, int *howclose ) { int *temp, a, b, c, i; *start = NULL; while ( *start==NULL ) // 开始计算列 { if ( Text[++Textloc]=='/0' ) // 不存在更多的text break; temp = Rdiff; // 交换,使得再次计算右边的列 Rdiff = Ldiff; Ldiff = temp; Rdiff[0] = 0; // 顶行,边界行 temp = Roff; // 交换偏移数组 Roff = Loff; Loff = temp; Roff[1] = 0; for (i=0; i<Plen; i++) { // 计算机相邻的元素 if( Pattern[i] == Text[Textloc] ) a=Ldiff[i]; else a=Ldiff[i]+1; b=Ldiff[i+1]+1; c=Rdiff[i]+1; //选择最小的 if (b<a) a=b; if(c<a) a=c; Rdiff[i+1] = a; } // 更新偏移数组 if (Plen>1) for (i=2;i<=Plen;i++) { if (Ldiff[i-1]<Rdiff[i]) Roff[i] = Loff[i-1]-1; else if (Rdiff[i-1]<Rdiff[i]) Roff[i] = Roff[i-1]; else if (Ldiff[i]<Rdiff[i]) Roff[i] = Loff[i]-1; else // Ldiff[i-1] == Rdiff[i] Roff[i] = Loff[i-1]-1; } if (Rdiff[Plen]<=Degree) //找到允许相似度内的串 { *end = Text+Textloc; *start = *end + Roff[Plen]; *howclose = Rdiff[Plen]; } } if (start == NULL) // 查找完成,释放内存 free(Ldiff); } int main(int argc, char *argv[]) { char *begin, *end; int howclose; if (argc!=4) { fprintf(stderr, "Usage is: approx pattern text degree/n" ); return EXIT_FAILURE; } AppInit(argv[1], argv[2], atoi(argv[3])); AppNext( &begin, &end, &howclose ); while( begin != NULL ) { // %.*s可以输出指定的长度的字符串,字符串不需要给定终止符'/0' printf( "Degree %d: %.*s/n", howclose, end-begin+1, begin ); AppNext( &begin, &end, &howclose ); } return EXIT_SUCCESS; }