两个字符串之间的(编辑(或)Levenshtein距离是将一个字符串转换为另一个字符串所需的最小数量的单字符插入、删除和替换。如果这两个字符串的长度各为n,则众所周知,这可以通过动态规划在O(n^2)时间内完成。下面的Python代码对两个字符串s1和s2执行此计算。
def edit_distance(s1, s2):
l1 = len(s1)
l2 = len(s2)
matrix = [range(l1 + 1)] * (l2 + 1)
for zz in range(l2 + 1):
matrix[zz] = range(zz,zz + l1 + 1)
for zz in range(0,l2):
for sz in range(0,l1):
if s1[sz] == s2[zz]:
matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz])
else:
matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz] + 1)
return matrix[l2][l1]在这个任务中,您必须尽可能接近计算编辑距离,但是内存有严格的限制。允许您的代码定义一个包含1000个32位整数的数组,这将是您在计算中使用的唯一临时存储。所有变量和数据结构都包含在这个数组中。特别是,对于长度为1000的字符串,您将无法实现上面的算法,因为它需要存储至少1,000,000个数字。如果您的语言没有32位整数(例如Python),您只需确保在数组中不存储大于2^32-1的数字。
您可以使用您选择的任何标准库读取数据,而不必担心该部分的内存限制。为了使代码的主要部分公平竞争,您只能使用与C编程语言中的操作在功能上等价的操作,并且不能使用任何外部库。
更清楚的是,存储输入数据或语言解释器、JVM等所使用的内存不计入限制,您可能不会将任何东西写入磁盘。您必须假设输入数据在内存中是只读的,因此不能重用它以获得更多的工作空间。
您的代码应该以下列格式读取文件。它将有三条线。第一行是真正的编辑距离。第二个是字符串1,第三个是字符串2,我将在https://bpaste.net/show/6905001d52e8用示例数据测试它,这里的字符串长度为10,000,但不应该专门用于这些数据。它应该输出两个字符串之间最小的编辑距离。
您还需要证明您的编辑距离实际上来自一组有效的编辑。您的代码应该有一个开关,将其转换为一种模式,可以使用更多的内存(随您喜欢),并输出给您编辑距离的编辑操作。
你的分数将是(optimal edit distance/divided by the edit distance you find) * 100。首先,请注意,只要计算两个字符串之间的不匹配数,就可以得到一个分数。
您可以使用任何您喜欢的语言,这些语言在Linux中是免费的,而且易于安装。
在打成平局的情况下,我将在我的Linux机器上运行您的代码,最快的代码将获胜。
发布于 2014-09-18 21:10:15
估计算法:该算法首先找到两个字符串不同的位置,然后尝试所有可能的N操作排列(插入、删除、替换-匹配的字符跳过而不使用操作)。它根据操作集成功匹配这两个字符串的距离,再加上它导致字符串长度收敛的程度,对每个可能的操作集进行评分。在确定N个操作的最高评分集之后,应用该集合中的第一个操作,找到下一个不匹配的操作,并且进程重复直到字符串的结束。
该程序尝试从1-10中的所有N值,并选择给出最佳结果的级别。N=10通常是最好的,因为评分方法考虑了字符串的长度。更高的N值可能会更好,但会花费更多的时间。
内存使用:由于程序是纯迭代的,所以它只需要很少的内存。只使用19个变量来跟踪程序状态。这些值由#defines设置为全局变量。
用法:程序与feersum的使用相同:假设第一个参数是文件,任何附加的参数都表示应该显示编辑。程序总是打印估计的编辑距离和分数。
验证输出:它格式化为三行的验证输出:
11011111100101100111100110100 110 0 0000 0 01101
R I IR R D D D DDD D D
01 1111110010 0001110001101000110101000011101011010顶部行是目标字符串,中间行是操作,底部是正在编辑的字符串。操作行中的空格表示字符匹配。'R‘表示编辑字符串的字符位于该位置,替换为目标字符串的字符。'I‘表示编辑字符串将目标字符串的字符插入到该位置。D‘表示编辑字符串中的字符已删除。当另一个字符串插入或删除字符时,编辑字符串和目标字符串具有插入空格,以便它们对齐。
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <math.h>
#include <fstream>
int memory[1000];
#define first (*(const char **)&memory[0])
#define second (*(const char **)&memory[1])
#define block_ia memory[2]
#define block_ib memory[3]
#define block_n memory[4]
#define block_op memory[5]
#define block_o memory[6]
#define block_x memory[7]
#define n memory[8]
#define opmax memory[9]
#define best_op memory[10]
#define best_score memory[11]
#define score memory[12]
#define best_counter memory[13]
#define la memory[14]
#define lb memory[15]
#define best memory[16]
#define bestn memory[17]
#define total memory[18]
// verification variables
char printline1[0xffff]={};
char *p1=printline1;
char printline2[0xffff]={};
char *p2=printline2;
char printline3[0xffff]={};
char *p3=printline3;
// determine how many characters match after a set of operations
int block(){
block_ia=0;
block_ib=0;
for ( block_x=0;block_x<block_n;block_x++){
block_o = block_op%3;
block_op /= 3;
if ( block_o == 0 ){ // replace
block_ia++;
block_ib++;
} else if ( block_o == 1 ){ // delete
block_ib++;
} else { // insert
if ( first[block_ia] ){
block_ia++;
}
}
while ( first[block_ia] && first[block_ia]==second[block_ib] ){ // find next mismatch
block_ia++;
block_ib++;
}
if ( first[block_ia]==0 ){
return block_x;
}
}
return block_n;
}
// find the highest-scoring set of N operations for the current string position
void bestblock(){
best_op=0;
best_score=0;
la = strlen(first);
lb = strlen(second);
block_n = n;
for(best_counter=0;best_counter<opmax;best_counter++){
block_op=best_counter;
score = n-block();
score += block_ia-abs((la-block_ia)-(lb-block_ib));
if ( score > best_score ){
best_score = score;
best_op = best_counter;
}
}
}
// prepare edit confirmation record
void printedit(const char * a, const char * b, int o){
o%=3;
if ( o == 0 ){ // replace
*p1 = *a;
if ( *b ){
*p2 = 'R';
*p3 = *b;
b++;
} else {
*p2 = 'I';
*p3 = ' ';
}
a++;
} else if ( o == 1 ){ // delete
*p1 = ' ';
*p2 = 'D';
*p3 = *b;
b++;
} else { // insert
*p1 = *a;
*p2 = 'I';
*p3 = ' ';
a++;
}
p1++;
p2++;
p3++;
while ( *a && *a==*b ){
*p1 = *a;
*p2 = ' ';
*p3 = *b;
p1++;
p2++;
p3++;
a++;
b++;
}
}
int main(int argc, char * argv[]){
if ( argc < 2 ){
printf("No file name specified\n");
return 0;
}
std::ifstream file(argv[1]);
std::string line0,line1,line2;
std::getline(file,line0);
std::getline(file,line1);
std::getline(file,line2);
// begin estimating Levenshtein distance
best = 0;
bestn = 0;
for ( n=1;n<=10;n++){ // n is the number of operations that can be in a test set
opmax = (int)pow(3.0,n);
first = line1.c_str();
second = line2.c_str();
while ( *first && *first == *second ){
first++;
second++;
}
total=0;
while ( *first && *second ){
bestblock();
block_n=1;
block_op=best_op;
block();
total ++;
first += block_ia;
second += block_ib;
}
// when one string is exhausted, all following ops must be insert or delete
while(*second){
total++;
second++;
}
while(*first){
total++;
first++;
}
if ( !best || total < best ){
best = total;
bestn = n;
}
}
// done estimating Levenshtein distance
// dump info to prove the edit distance actually comes from a valid set of edits
if ( argc >= 3 ){
p1 = printline1;
p2 = printline2;
p3 = printline3;
n = bestn;
opmax = (int)pow(3.0,n);
first = line1.c_str();
second = line2.c_str();
while ( *first && *first == *second ){
*p1 = *first;
*p2 = ' ';
*p3 = *second;
p1++;
p2++;
p3++;
first++;
second++;
}
while ( *first && *second){
bestblock();
block_n=1;
block_op=best_op;
block();
printedit(first,second,best_op);
first += block_ia;
second += block_ib;
}
while(*second){
*p1=' ';
*p2='D';
*p3=*second;
p1++;
p2++;
p3++;
second++;
}
while(*first){
*p1=*first;
*p2='I';
*p3=' ';
p1++;
p2++;
p3++;
first++;
}
p1 = printline1;
p2 = printline2;
p3 = printline3;
int ins=0;
int del=0;
int rep=0;
while ( *p1 ){
int a;
for ( a=0;a<79&&p1[a];a++)
printf("%c",p1[a]);
printf("\n");
p1+=a;
for ( a=0;a<79&&p2[a];a++){
ins += ( p2[a] == 'I' );
del += ( p2[a] == 'D' );
rep += ( p2[a] == 'R' );
printf("%c",p2[a]);
}
printf("\n");
p2+=a;
for ( a=0;a<79&&p3[a];a++)
printf("%c",p3[a]);
printf("\n\n");
p3+=a;
}
printf("Best N=%d\n",bestn);
printf("Inserted = %d, Deleted = %d, Replaced=%d, Total = %d\nLength(line1)=%d, Length(Line2)+ins-del=%d\n",ins,del,rep,ins+del+rep,line1.length(),line2.length()+ins-del);
}
printf("%d, Score = %0.2f\n",best,2886*100.0/best);
system("pause");
return 0;
}发布于 2014-09-15 02:58:40
该程序设计用于处理任意文本字符串。只要两者都不超过13824个字符,它们就可以有任何不同的长度。它使用1,897个16位整数,相当于949个32位整数.一开始我是用C写的,但后来意识到没有读取一行的功能。
第一个命令行参数应该是一个文件名。如果存在第二个参数,则打印编辑摘要。忽略文件中的第一行,而第二和第三行是字符串。
该算法是通常算法的双重阻塞版本。它执行的操作数量基本相同,但当然不太准确,因为如果一个公共子序列被分割到块的边缘,那么可能节省的部分就会丢失。
#include <cstring>
#include <inttypes.h>
#include <iostream>
#include <fstream>
#define M 24
#define MAXLEN (M*M*M)
#define SETMIN(V, X) if( (X) < (V) ) { (V) = (X); }
#define MIN(X, Y) ( (X) < (Y) ? (X) : (Y) )
char A[MAXLEN+1], B[MAXLEN+1];
uint16_t d0[M+1][M+1], d1[M+1][M+1], d2[M+1][M+1];
int main(int argc, char**argv)
{
if(argc < 2)
return 1;
std::ifstream fi(argv[1]);
std::string Astr, Bstr;
for(int i = 3; i--;)
getline(fi, i?Bstr:Astr);
if(!fi.good()) {
printf("Error reading file");
return 5;
}
if(Astr.length() > MAXLEN || Bstr.length() > MAXLEN) {
printf("String too long");
return 7;
}
strcpy(A, Astr.c_str());
strcpy(B, Bstr.c_str());
uint16_t lA = Astr.length(), lB = Bstr.length();
if(!lA || !lB) {
printf("%d\n", lA|lB);
return 0;
}
uint16_t nbA2, nbB2, bA2, bB2, nbA1, nbB1, bA1, bB1, nbA0, nbB0, bA0, bB0; //block, number of blocks
uint16_t iA2, iB2, iA1, iB1, jA2, jB2, jA1, jB1; //start, end indices of block
nbA2 = MIN(M, lA);
nbB2 = MIN(M, lB);
for(bA2 = 0; bA2 <= nbA2; bA2++) {
iA2 = lA * (bA2-1)/nbA2, jA2 = lA * bA2/nbA2;
for(bB2 = 0; bB2 <= nbB2; bB2++) {
if(!(bA2|bB2)) {
d2[0][0] = 0;
continue;
}
iB2 = lB * (bB2-1)/nbB2, jB2 = lB * bB2/nbB2;
d2[bA2][bB2] = ~0;
if(bB2)
SETMIN(d2[bA2][bB2], d2[bA2][bB2-1] + (jB2-iB2));
if(bA2)
SETMIN(d2[bA2][bB2], d2[bA2-1][bB2] + (jA2-iA2));
if(bA2 && bB2) {
nbA1 = MIN(M, jA2-iA2);
nbB1 = MIN(M, jB2-iB2);
for(bA1 = 0; bA1 <= nbA1; bA1++) {
iA1 = iA2 + (jA2-iA2) * (bA1-1)/nbA1, jA1 = iA2 + (jA2-iA2) * bA1/nbA1;
for(bB1 = 0; bB1 <= nbB1; bB1++) {
if(!(bA1|bB1)) {
d1[0][0] = 0;
continue;
}
iB1 = iB2 + (jB2-iB2) * (bB1-1)/nbB1, jB1 = iB2 + (jB2-iB2) * bB1/nbB1;
d1[bA1][bB1] = ~0;
if(bB1)
SETMIN(d1[bA1][bB1], d1[bA1][bB1-1] + (jB1-iB1));
if(bA1)
SETMIN(d1[bA1][bB1], d1[bA1-1][bB1] + (jA1-iA1));
if(bA1 && bB1) {
nbA0 = jA1-iA1;
nbB0 = jB1-iB1;
for(bA0 = 0; bA0 <= nbA0; bA0++) {
for(bB0 = 0; bB0 <= nbB0; bB0++) {
if(!(bA0|bB0)) {
d0[0][0] = 0;
continue;
}
d0[bA0][bB0] = ~0;
if(bB0)
SETMIN(d0[bA0][bB0], d0[bA0][bB0-1] + 1);
if(bA0)
SETMIN(d0[bA0][bB0], d0[bA0-1][bB0] + 1);
if(bA0 && bB0)
SETMIN(d0[bA0][bB0], d0[bA0-1][bB0-1] + (A[iA1 + nbA0 - 1] != B[iB1 + nbB0 - 1]));
}
}
SETMIN(d1[bA1][bB1], d1[bA1-1][bB1-1] + d0[nbA0][nbB0]);
}
}
}
SETMIN(d2[bA2][bB2], d2[bA2-1][bB2-1] + d1[nbA1][nbB1]);
}
}
}
printf("%d\n", d2[nbA2][nbB2]);
if(argc == 2)
return 0;
int changecost, total = 0;
for(bA2 = nbA2, bB2 = nbB2; bA2||bB2; ) {
iA2 = lA * (bA2-1)/nbA2, jA2 = lA * bA2/nbA2;
iB2 = lB * (bB2-1)/nbB2, jB2 = lB * bB2/nbB2;
if(bB2 && d2[bA2][bB2-1] + (jB2-iB2) == d2[bA2][bB2]) {
total += changecost = (jB2-iB2);
char tmp = B[jB2];
B[jB2] = 0;
printf("%d %d deleted {%s}\n", changecost, total, B + iB2);
B[jB2] = tmp;
--bB2;
} else if(bA2 && d2[bA2-1][bB2] + (jA2-iA2) == d2[bA2][bB2]) {
total += changecost = (jA2-iA2);
char tmp = B[jA2];
A[jA2] = 0;
printf("%d %d inserted {%s}\n", changecost, total, A + iA2);
A[jA2] = tmp;
--bA2;
} else {
total += changecost = d2[bA2][bB2] - d2[bA2-1][bB2-1];
char tmpa = A[jA2], tmpb = B[jB2];
B[jB2] = A[jA2] = 0;
printf("%d %d changed {%s} to {%s}\n", changecost, total, B + iB2, A + iA2);
A[jA2] = tmpa, B[jB2] = tmpb;
--bA2, --bB2;
}
}
return 0;
}https://codegolf.stackexchange.com/questions/37620
复制相似问题