Qt 4.6
[qstardict] / plugins / stardict / distance.cpp
1 /*
2    writer : Opera Wang
3    E-Mail : wangvisual AT sohu DOT com
4    License: GPL
5 */
6
7 /* filename: distance.cc */
8 /*
9 http://www.merriampark.com/ld.htm
10 What is Levenshtein Distance?
11  
12 Levenshtein distance (LD) is a measure of the similarity between two strings, 
13 which we will refer to as the source string (s) and the target string (t). 
14 The distance is the number of deletions, insertions, or substitutions required
15  to transform s into t. For example,
16  
17     * If s is "test" and t is "test", then LD(s,t) = 0, because no transformations are needed. 
18     The strings are already identical.
19     * If s is "test" and t is "tent", then LD(s,t) = 1, because one substitution
20      (change "s" to "n") is sufficient to transform s into t.
21  
22 The greater the Levenshtein distance, the more different the strings are.
23  
24 Levenshtein distance is named after the Russian scientist Vladimir Levenshtein,
25  who devised the algorithm in 1965. If you can't spell or pronounce Levenshtein,
26  the metric is also sometimes called edit distance.
27  
28 The Levenshtein distance algorithm has been used in:
29  
30     * Spell checking
31     * Speech recognition
32     * DNA analysis
33     * Plagiarism detection 
34 */
35
36
37 #include <stdlib.h>
38 #include <string.h> 
39 //#include <stdio.h>
40
41 #include "distance.h"
42
43 #define OPTIMIZE_ED 
44 /*
45 Cover transposition, in addition to deletion,
46 insertion and substitution. This step is taken from:
47 Berghel, Hal ; Roach, David : "An Extension of Ukkonen's 
48 Enhanced Dynamic Programming ASM Algorithm"
49 (http://www.acm.org/~hlb/publications/asm/asm.html)
50 */
51 #define COVER_TRANSPOSITION
52
53 /****************************************/
54 /*Implementation of Levenshtein distance*/
55 /****************************************/
56
57 EditDistance::EditDistance()
58 {
59     currentelements = 2500; // It's enough for most conditions :-)
60     d = (int*)malloc(sizeof(int) * currentelements);
61 }
62
63 EditDistance::~EditDistance()
64 {
65     //    printf("size:%d\n",currentelements);
66     if (d)
67         free(d);
68 }
69
70 #ifdef OPTIMIZE_ED
71 int EditDistance::CalEditDistance(const gunichar *s, const gunichar *t, const int limit)
72 /*Compute levenshtein distance between s and t, this is using QUICK algorithm*/
73 {
74     int n = 0, m = 0, iLenDif, k, i, j, cost;
75     // Remove leftmost matching portion of strings
76     while ( *s && (*s == *t) )
77     {
78         s++;
79         t++;
80     }
81
82     while (s[n])
83     {
84         n++;
85     }
86     while (t[m])
87     {
88         m++;
89     }
90
91     // Remove rightmost matching portion of strings by decrement n and m.
92     while ( n && m && (*(s + n - 1) == *(t + m - 1)) )
93     {
94         n--;
95         m--;
96     }
97     if ( m == 0 || n == 0 || d == (int*)0 )
98         return (m + n);
99     if ( m < n )
100     {
101         const gunichar * temp = s;
102         int itemp = n;
103         s = t;
104         t = temp;
105         n = m;
106         m = itemp;
107     }
108     iLenDif = m - n;
109     if ( iLenDif >= limit )
110         return iLenDif;
111     // step 1
112     n++;
113     m++;
114     //    d=(int*)malloc(sizeof(int)*m*n);
115     if ( m*n > currentelements )
116     {
117         currentelements = m * n * 2;    // double the request
118         d = (int*)realloc(d, sizeof(int) * currentelements);
119         if ( (int*)0 == d )
120             return (m + n);
121     }
122     // step 2, init matrix
123     for (k = 0;k < n;k++)
124         d[k] = k;
125     for (k = 1;k < m;k++)
126         d[k*n] = k;
127     // step 3
128     for (i = 1;i < n;i++)
129     {
130         // first calculate column, d(i,j)
131         for ( j = 1;j < iLenDif + i;j++ )
132         {
133             cost = s[i - 1] == t[j - 1] ? 0 : 1;
134             d[j*n + i] = minimum(d[(j - 1) * n + i] + 1, d[j * n + i - 1] + 1, d[(j - 1) * n + i - 1] + cost);
135 #ifdef COVER_TRANSPOSITION
136
137             if ( i >= 2 && j >= 2 && (d[j*n + i] - d[(j - 2)*n + i - 2] == 2)
138                     && (s[i - 2] == t[j - 1]) && (s[i - 1] == t[j - 2]) )
139                 d[j*n + i]--;
140 #endif
141
142         }
143         // second calculate row, d(k,j)
144         // now j==iLenDif+i;
145         for ( k = 1;k <= i;k++ )
146         {
147             cost = s[k - 1] == t[j - 1] ? 0 : 1;
148             d[j*n + k] = minimum(d[(j - 1) * n + k] + 1, d[j * n + k - 1] + 1, d[(j - 1) * n + k - 1] + cost);
149 #ifdef COVER_TRANSPOSITION
150
151             if ( k >= 2 && j >= 2 && (d[j*n + k] - d[(j - 2)*n + k - 2] == 2)
152                     && (s[k - 2] == t[j - 1]) && (s[k - 1] == t[j - 2]) )
153                 d[j*n + k]--;
154 #endif
155
156         }
157         // test if d(i,j) limit gets equal or exceed
158         if ( d[j*n + i] >= limit )
159         {
160             return d[j*n + i];
161         }
162     }
163     // d(n-1,m-1)
164     return d[n*m - 1];
165 }
166 #else
167 int EditDistance::CalEditDistance(const char *s, const char *t, const int limit)
168 {
169     //Step 1
170     int k, i, j, n, m, cost;
171     n = strlen(s);
172     m = strlen(t);
173     if ( n != 0 && m != 0 && d != (int*)0 )
174     {
175         m++;
176         n++;
177         if ( m*n > currentelements )
178         {
179             currentelements = m * n * 2;
180             d = (int*)realloc(d, sizeof(int) * currentelements);
181             if ( (int*)0 == d )
182                 return (m + n);
183         }
184         //Step 2
185         for (k = 0;k < n;k++)
186             d[k] = k;
187         for (k = 0;k < m;k++)
188             d[k*n] = k;
189         //Step 3 and 4
190         for (i = 1;i < n;i++)
191             for (j = 1;j < m;j++)
192             {
193                 //Step 5
194                 if (s[i - 1] == t[j - 1])
195                     cost = 0;
196                 else
197                     cost = 1;
198                 //Step 6
199                 d[j*n + i] = minimum(d[(j - 1) * n + i] + 1, d[j * n + i - 1] + 1, d[(j - 1) * n + i - 1] + cost);
200 #ifdef COVER_TRANSPOSITION
201
202                 if ( i >= 2 && j >= 2 && (d[j*n + i] - d[(j - 2)*n + i - 2] == 2)
203                         && (s[i - 2] == t[j - 1]) && (s[i - 1] == t[j - 2]) )
204                     d[j*n + i]--;
205 #endif
206
207             }
208         return d[n*m - 1];
209     }
210     else
211         return (n + m);
212 }
213 #endif