levenshtein

(PHP 4 >= 4.0.1, PHP 5, PHP 7)

levenshteinCalculate Levenshtein distance between two strings

Descrierea

int levenshtein ( string $str1 , string $str2 )
int levenshtein ( string $str1 , string $str2 , int $cost_ins , int $cost_rep , int $cost_del )

The Levenshtein distance is defined as the minimal number of characters you have to replace, insert or delete to transform str1 into str2. The complexity of the algorithm is O(m*n), where n and m are the length of str1 and str2 (rather good when compared to similar_text(), which is O(max(n,m)**3), but still expensive).

In its simplest form the function will take only the two strings as parameter and will calculate just the number of insert, replace and delete operations needed to transform str1 into str2.

A second variant will take three additional parameters that define the cost of insert, replace and delete operations. This is more general and adaptive than variant one, but not as efficient.

Parametri

str1

One of the strings being evaluated for Levenshtein distance.

str2

One of the strings being evaluated for Levenshtein distance.

cost_ins

Defines the cost of insertion.

cost_rep

Defines the cost of replacement.

cost_del

Defines the cost of deletion.

Valorile întoarse

This function returns the Levenshtein-Distance between the two argument strings or -1, if one of the argument strings is longer than the limit of 255 characters.

Exemple

Example #1 levenshtein() example

<?php
// input misspelled word
$input 'carrrot';

// array of words to check against
$words  = array('apple','pineapple','banana','orange',
                
'radish','carrot','pea','bean','potato');

// no shortest distance found, yet
$shortest = -1;

// loop through words to find the closest
foreach ($words as $word) {

    
// calculate the distance between the input word,
    // and the current word
    
$lev levenshtein($input$word);

    
// check for an exact match
    
if ($lev == 0) {

        
// closest word is this one (exact match)
        
$closest $word;
        
$shortest 0;

        
// break out of the loop; we've found an exact match
        
break;
    }

    
// if this distance is less than the next found shortest
    // distance, OR if a next shortest word has not yet been found
    
if ($lev <= $shortest || $shortest 0) {
        
// set the closest match, and shortest distance
        
$closest  $word;
        
$shortest $lev;
    }
}

echo 
"Input word: $input\n";
if (
$shortest == 0) {
    echo 
"Exact match found: $closest\n";
} else {
    echo 
"Did you mean: $closest?\n";
}

?>

Exemplul de mai sus va afișa:

Input word: carrrot
Did you mean: carrot?

A se vedea și

add a note add a note

User Contributed Notes 28 notes

up
41
luciole75w at no dot spam dot gmail dot com
4 years ago
The levenshtein function processes each byte of the input string individually. Then for multibyte encodings, such as UTF-8, it may give misleading results.

Example with a french accented word :
- levenshtein('notre', 'votre') = 1
- levenshtein('notre', 'nôtre') = 2 (huh ?!)

You can easily find a multibyte compliant PHP implementation of the levenshtein function but it will be of course much slower than the C implementation.

Another option is to convert the strings to a single-byte (lossless) encoding so that they can feed the fast core levenshtein function.

Here is the conversion function I used with a search engine storing UTF-8 strings, and a quick benchmark. I hope it will help.

<?php
// Convert an UTF-8 encoded string to a single-byte string suitable for
// functions such as levenshtein.
//
// The function simply uses (and updates) a tailored dynamic encoding
// (in/out map parameter) where non-ascii characters are remapped to
// the range [128-255] in order of appearance.
//
// Thus it supports up to 128 different multibyte code points max over
// the whole set of strings sharing this encoding.
//
function utf8_to_extended_ascii($str, &$map)
{
   
// find all multibyte characters (cf. utf-8 encoding specs)
   
$matches = array();
    if (!
preg_match_all('/[\xC0-\xF7][\x80-\xBF]+/', $str, $matches))
        return
$str; // plain ascii string
   
    // update the encoding map with the characters not already met
   
foreach ($matches[0] as $mbc)
        if (!isset(
$map[$mbc]))
           
$map[$mbc] = chr(128 + count($map));
   
   
// finally remap non-ascii characters
   
return strtr($str, $map);
}

// Didactic example showing the usage of the previous conversion function but,
// for better performance, in a real application with a single input string
// matched against many strings from a database, you will probably want to
// pre-encode the input only once.
//
function levenshtein_utf8($s1, $s2)
{
   
$charMap = array();
   
$s1 = utf8_to_extended_ascii($s1, $charMap);
   
$s2 = utf8_to_extended_ascii($s2, $charMap);
   
    return
levenshtein($s1, $s2);
}
?>

Results (for about 6000 calls)
- reference time core C function (single-byte) : 30 ms
- utf8 to ext-ascii conversion + core function : 90 ms
- full php implementation : 3000 ms
up
5
Johan Gennesson php at genjo dot fr
1 year ago
Please, be aware that:

<?php
// Levenshtein Apostrophe (U+0027 &#39;) and Right Single Quotation Mark (U+2019 &#8217;)
echo levenshtein("'", "’");
?>

will output 3!
up
18
paulrowe at iname dot com
9 years ago
[EDITOR'S NOTE: original post and 2 corrections combined into 1 -- mgf]

Here is an implementation of the Levenshtein Distance calculation that only uses a one-dimensional array and doesn't have a limit to the string length. This implementation was inspired by maze generation algorithms that also use only one-dimensional arrays.

I have tested this function with two 532-character strings and it completed in 0.6-0.8 seconds.

<?php
/*
* This function starts out with several checks in an attempt to save time.
*   1.  The shorter string is always used as the "right-hand" string (as the size of the array is based on its length). 
*   2.  If the left string is empty, the length of the right is returned.
*   3.  If the right string is empty, the length of the left is returned.
*   4.  If the strings are equal, a zero-distance is returned.
*   5.  If the left string is contained within the right string, the difference in length is returned.
*   6.  If the right string is contained within the left string, the difference in length is returned.
* If none of the above conditions were met, the Levenshtein algorithm is used.
*/
function LevenshteinDistance($s1, $s2)
{
 
$sLeft = (strlen($s1) > strlen($s2)) ? $s1 : $s2;
 
$sRight = (strlen($s1) > strlen($s2)) ? $s2 : $s1;
 
$nLeftLength = strlen($sLeft);
 
$nRightLength = strlen($sRight);
  if (
$nLeftLength == 0)
    return
$nRightLength;
  else if (
$nRightLength == 0)
    return
$nLeftLength;
  else if (
$sLeft === $sRight)
    return
0;
  else if ((
$nLeftLength < $nRightLength) && (strpos($sRight, $sLeft) !== FALSE))
    return
$nRightLength - $nLeftLength;
  else if ((
$nRightLength < $nLeftLength) && (strpos($sLeft, $sRight) !== FALSE))
    return
$nLeftLength - $nRightLength;
  else {
   
$nsDistance = range(1, $nRightLength + 1);
    for (
$nLeftPos = 1; $nLeftPos <= $nLeftLength; ++$nLeftPos)
    {
     
$cLeft = $sLeft[$nLeftPos - 1];
     
$nDiagonal = $nLeftPos - 1;
     
$nsDistance[0] = $nLeftPos;
      for (
$nRightPos = 1; $nRightPos <= $nRightLength; ++$nRightPos)
      {
       
$cRight = $sRight[$nRightPos - 1];
       
$nCost = ($cRight == $cLeft) ? 0 : 1;
       
$nNewDiagonal = $nsDistance[$nRightPos];
       
$nsDistance[$nRightPos] =
         
min($nsDistance[$nRightPos] + 1,
             
$nsDistance[$nRightPos - 1] + 1,
             
$nDiagonal + $nCost);
       
$nDiagonal = $nNewDiagonal;
      }
    }
    return
$nsDistance[$nRightLength];
  }
}
?>
up
6
WiLDRAGoN
2 years ago
Some small changes allow you to calculate multiple words.

<?php

$input
= array();
$dictionary  = array();
foreach (
$input as $output) {
   
$shortest = -1;
    foreach (
$dictionary as $word) {
       
$lev = levenshtein($output, $word);
        if (
$lev == 0) {
           
$closest = $word;
           
$shortest = 0;
        }
        if (
$lev <= $shortest || $shortest < 0) {
           
$closest  = $word;
           
$shortest = $lev;
        }
    }
    echo
"Input word: $output\n";
    if (
$shortest == 0) {
        echo
"Exact match found: $closest\n";
    } else {
        echo
"Did you mean: $closest?\n";
    }
}

?>
up
3
bisqwit at iki dot fi
15 years ago
At the time of this manual note the user defined thing 
in levenshtein() is not implemented yet. I wanted something
like that, so I wrote my own function. Note that this
doesn't return levenshtein() difference, but instead
an array of operations to transform a string to another.

Please note that the difference finding part (resync)
may be extremely slow on long strings.

<?php

/* matchlen(): returns the length of matching
* substrings at beginning of $a and $b
*/
function matchlen(&$a, &$b)
{
 
$c=0;
 
$alen = strlen($a);
 
$blen = strlen($b);
 
$d = min($alen, $blen);
  while(
$a[$c] == $b[$c] && $c < $d)
   
$c++;  
  return
$c;
}

/* Returns a table describing
* the differences of $a and $b */
function calcdiffer($a, $b)
{
 
$alen = strlen($a);
 
$blen = strlen($b);
 
$aptr = 0;
 
$bptr = 0;
 
 
$ops = array();
 
  while(
$aptr < $alen && $bptr < $blen)
  {
   
$matchlen = matchlen(substr($a, $aptr), substr($b, $bptr));
    if(
$matchlen)
    {
     
$ops[] = array('=', substr($a, $aptr, $matchlen));
     
$aptr += $matchlen;
     
$bptr += $matchlen;
      continue;
    }
   
/* Difference found */
    
   
$bestlen=0;
   
$bestpos=array(0,0);
    for(
$atmp = $aptr; $atmp < $alen; $atmp++)
    {
      for(
$btmp = $bptr; $btmp < $blen; $btmp++)
      {
       
$matchlen = matchlen(substr($a, $atmp), substr($b, $btmp));
        if(
$matchlen>$bestlen)
        {
         
$bestlen=$matchlen;
         
$bestpos=array($atmp,$btmp);
        }
        if(
$matchlen >= $blen-$btmp)break;
      }
    }
    if(!
$bestlen)break;
  
   
$adifflen = $bestpos[0] - $aptr;
   
$bdifflen = $bestpos[1] - $bptr;

    if(
$adifflen)
    {
     
$ops[] = array('-', substr($a, $aptr, $adifflen));
     
$aptr += $adifflen;
    }
    if(
$bdifflen)
    {
     
$ops[] = array('+', substr($b, $bptr, $bdifflen));
     
$bptr += $bdifflen;
    }
   
$ops[] = array('=', substr($a, $aptr, $bestlen));
   
$aptr += $bestlen;
   
$bptr += $bestlen;
  }
  if(
$aptr < $alen)
  {
   
/* b has too much stuff */
   
$ops[] = array('-', substr($a, $aptr));
  }
  if(
$bptr < $blen)
  {
   
/* a has too little stuff */
   
$ops[] = array('+', substr($b, $bptr));
  }
  return
$ops;
}


Example:

$tab = calcdiffer('Tm on jonkinlainen testi',
                 
'Tm ei ole minknlainen testi.'); 
$ops = array('='=>'Ok', '-'=>'Remove', '+'=>'Add');
foreach(
$tab as $k)
  echo
$ops[$k[0]], " '", $k[1], "'\n";

Example output:

Ok 'Tm '
Remove 'on jonki'
Add 'ei ole mink'
Ok 'nlainen testi'
Add '.'
up
8
dschultz at protonic dot com
17 years ago
It's also useful if you want to make some sort of registration page and you want to make sure that people who register don't pick usernames that are very similar to their passwords.
up
3
gzink at zinkconsulting dot com
14 years ago
Try combining this with metaphone() for a truly amazing fuzzy search function. Play with it a bit, the results can be plain scary (users thinking the computer is almost telepathic) when implemented properly. I wish spell checkers worked as well as the code I've written.

I would release my complete code if reasonable, but it's not, due to copyright issues. I just hope that somebody can learn from this little tip!
up
6
justin at visunet dot ie
12 years ago
<?php

   
/*********************************************************************
    * The below func, btlfsa, (better than levenstien for spelling apps)
    * produces better results when comparing words like haert against
    * haart and heart.
    *
    * For example here is the output of levenshtein compared to btlfsa
    * when comparing 'haert' to 'herat, haart, heart, harte'
    *
    * btlfsa('haert','herat'); output is.. 3
    * btlfsa('haert','haart'); output is.. 3
    * btlfsa('haert','harte'); output is.. 3
    * btlfsa('haert','heart'); output is.. 2
    *
    * levenshtein('haert','herat'); output is.. 2
    * levenshtein('haert','haart'); output is.. 1
    * levenshtein('haert','harte'); output is.. 2
    * levenshtein('haert','heart'); output is.. 2
    *
    * In other words, if you used levenshtein, 'haart' would be the
    * closest match to 'haert'. Where as, btlfsa sees that it should be
    * 'heart'
    */

   
function btlfsa($word1,$word2)
    {
       
$score = 0;

       
// For each char that is different add 2 to the score
        // as this is a BIG difference

       
$remainder  = preg_replace("/[".preg_replace("/[^A-Za-z0-9\']/",' ',$word1)."]/i",'',$word2);
       
$remainder .= preg_replace("/[".preg_replace("/[^A-Za-z0-9\']/",' ',$word2)."]/i",'',$word1);
       
$score      = strlen($remainder)*2;

       
// Take the difference in string length and add it to the score
       
$w1_len  = strlen($word1);
       
$w2_len  = strlen($word2);
       
$score 2$remainder  >  de>
eyword">) "usernotes-voteu">up
) "usernopantte" id="117651">
$sloc>in leclass="keyword">= = = = =   &nspan>)*2[0$word2);
       
$score 2&niv>
        $w1_len  [0$word2);
       
$score 2&"ass="keyword">[
&keyword">as $w1_len&nbclass="keyword">< [,$word2
($a$word1);
strlenbtlfsa&n !issetpan>);
$tab strlen<]) p;       }
&n nbsp;       $tab strlen<] !ass="default">0
$tab strlen<] btlfsa$w2_len  = strlen) {
        &s
=arra}"defaun>$w1_len&nbdefault">substr= strlen
com37953>;

">substr
= as like tseom765sevenstsuggove levehr claback by seywoex or53"> ¶.p;   * The below">substr= ' 'as as ,aing ba. 1substre/span>= $bptr substr= <>'+'= btlfsa(e/bsp;       $tab strlen<] nary as $word) miss
' '
,strlen$bptr ));
      $word) time of th_ord">e/bsp;       [= "/[^faulbn>e/sbye time of th...faul/bn>;
    } else {[
    $ time of th_ord">e/bsp;       $bptr "/[^faul/pren>" '"n class="defautitle="2005-04-05 07:46">,aing ba. 1$oduces_ord">e/span>= $bptr substr= <>'+'= btlfsa(e/bsp;       $tab strlen<] nary ' ',strlen$bptr ));
      $word) oduces_ord">e/bsp;       [= "/[^faulbn>e/sbyeoduces...faul/bn>;
    } else {[
    $oduces_ord">e/bsp;       $bptr "/[^faul/pren>" '"n class="string">"Did you mean:
Add '.'up
0796did="Vd51600"> down
6-10-28
1a href="#7658" class="name"> an array of o$distanan>.= ,as $word' ',"Exact match f$distanan>n clas="keyw/span><="keyO'); os: "5", user defof "1". Iput ism765flass="ht/>
It's also useful if you want to make some so107114default">Ok
<107114">Add '.'
<107114">Add '.'up
107114did="Vd51600"> down
/div1-08 <8:05>
v>   be theitivity" hrefeknt">/*.ode>reom765circumstanan*******10 09:01"span cdefaultfabsp;ihrefektrbr /f="#bsp; apps)"saw viii"<"iv 9:r spelbs.. 2
$word1);
);
foreach(
ass="keyword">[[[itivity sp;       substr= as );
= <>'+'= sp;       ([0as $word) in[[= sp;       &nass="keyword">([,$word2([0= sp;       substr= ,$word2&nass="keyword">([an ass="default">0= &ndss=movespan>= $a$word2(0= sp;       substr= ,substr= 0itivity sp;       < < $word2substr= as substr' ',' ',' ',' 'substr' ',as ,' '
,as
,' ''Add'$word1);
);
foreach(
ass="keyword">[

Exa= sp;       [n class="string">"Did you mean: Ok
<118906">Add '.'
118906did="Vd51600"> down
/6-02-25 05:08>
> < href="#24289" class="name"> in UTF-8. ">$k600"> ¶substras = n class="string">"Did you mean: Ok
<106381">Add '.'
<106381">Add '.'up
106381did="Vd51640"> /1-/1-02 11:40>
6> $k<"aemove om37953n> 600"> ¶= ="keyw/span> in= as substr' ',' ',' ',' 'substr' ',as ,' ',as ,' ' *******">substr= as );
= <>'+'= sp;       n class="defautitle="2005-04-05 07:46">&nass="keyword">(= 0= sp;       n class="defaun class="defautitle="2005-04-05 07:46">([0= sp;       substr= < $word2n class="defaun class="defautitle="2005-04-05 07:46">
$sd">= ="keyp; * The below">substr[0as $word) in[[= sp;      
&nass="keyword">([an ass="default">0= &ndss=movespan>= $a$word2n class="defaun class="defautitle="2005-04-05 07:46">(0= sp;       substr= < "Exact match fIn
3-07-11 10:22>
14a href="#7658" class="name"> sltz at protonic dot comcom379is; be theielina>in lppn0my clie01's ikeaba. ein() differeAfommeren cevrds a/>/*sfto aing values, s loopviv 9ithscla><, s can't blocksa;< < 9:01sbto, but a/>uggove lv="defsplayedzalo***efekre'sdi div cla clie01 9ithsa; spknsh.", fhl It's also useful if'
fault">Ok ss="votes">
<63274">Add '.'
6- 3- 7a03:18>
1a href="#7658" class="name"> sltz at protonic dot com$rent"ynr t;n>600"> ¶kdiv citudbs/(Findssa; un blockdiv clir; (numb$rebys sg>rr es ("2003-1window)    * closest mlt"_nt"ynr hn class="keyword">[) to 'haert'. Where as, btlfsa s="keyword">,,$word2,,$word2,$word2n class="defaun class="defautitle="2005-">(

$bptr < [,$word2,[,$word1);,[,$word1);,[,$word2,[,$word2as ,);[as as
strlen<600"p;       }
[
strlenbtlfsa$w2_len  [,strlen[) {
        <">$w2_len 
[);[[,$word1);,[,[[,$word1);,' ',
$w2_len  [,$word2);[,,strlen<}ault">$bptr $w2_len  $word1,$word2$bptr $w2_len&nbwhile&nass="keyword">([,$word2n class="defaun class="defauptr $w2_len  $word1,as $w2_len  [,strlen<=s="keyrds, if you usn class="defauptr $w2_len  [,[$w2_len  [,strlen<=s="keyrds, if you usn class="defauptr $w2_len&nbwhile&n(++p;       }
strlen<600"p;       }
[
'ng">' (++p;       }
[
[n class="defaun class="defauptr $w2_len&nb_len&nb_len&nb_len&nb_len&nbde&nass="keyword">(,strlen<}!as="keyword">,,[$word1$w2_len&nb_len&nb_len&nb_len&nb_len&nbde&nass="keyword">($word1$word2[,[$word1,[as [,$word2);[,,strlen<}="keyword">,$word1$bptr $w2_len&nb$w2_len&nb_len&nb_len&nb_len&nb_len&nbebsp;{her words, if you usstrlen$word1[,[[,$word1[,[$w2_len&nb_len&nb_len&nb_len&nb_len&nban claifflen$w2_len&nblass="k,$word2);$word1);[as ,$word2,[,$word2,[as ,$word2,[,$word2,$word2
,[$bptr [,$word2$bptr [,$word2$bptr < [,$word2,$word1,$word2);$word1$word1$word1$word1btlfsa$w2_len&nbde&nass="keyword">($word1$word1$word2,[($word1$word1as ,[[as ,[,$word1$word1$word2[,[,$word1$word1as [[,$word1$word1$word2[,$word1$word1as $w2_len&nb_len&nb_len&nb_len&nb_len&nbebsp;{her words, if you us[as ,[,$word1$word1$word2[,[,$word2[,$word1$word1$word2$w2_len&nblass="k$w2_len&nbebspde&nass="keyword">($word1$word1as ,[$w2_len  [as ,[,$word2,[,$word1$word1as $word2[,$word1$word1as $w2_len&nblass="klt">$bptr $w2_len  [as ,$word1$word1$word2,$word1$word1,$word1$word1as ,$word1$word1$bptr $w2_len  [,$word1$word1$bptr $w2_len  [,$word1$word1$bptr
[,[[,[[as ,[,[[,[,[$word2[as ,[,[[,[,$word2([,[[as ,[,$word2,[,[$word2< oss="keyword">[< "Did you mean:
5- 3-07 09:01>
2a href="#7658" class="name"> sltz at protonic dot comcom379e Llass="stri3>/*sein() differeI hav="a "2003-1p2000aor peoensspknshs. Iser"a SOUNDEX() "2003-1o**amazknsh in0mysql. MySQL SOUNDEX() 9illspera>iel79e Llass="stri3>/*sfwons pramaz"2003-1an claein() differeI0caneamaz" clrmy nt">/*sfaor efsplayk/*sfairstein() differe="keyword">< ¶
[(/*sss="keyword">[" '"[(" '"(as " '"as as ," '"as 'ng">' s="keyword">,$word2as " '"as < [< to 'haert'. Where as, btlfsa s[p;     &nbp; as (as ," '"as &lp; as
(as $a" '"as as
" '"< "Did you mean:
ss="votes"> 121644>nction.lno an>n>nshtein&vote=down" title="Votp; ounk likgmailsernocomvoted">down
17-09-16 12:24>
2 monthf="#7658" class="name"> sltz at protonic dot com= = fabspaifflen= = $waifflen=ss="key}sg">"Did you mean:
ss="votes"> 121121>nction.lno an>n>nshtein&vote=down" title="VotAnonymousvoted">down
17-05-23 12:04>
6 monthf="#7658" class="name"> sltz at protonic dot com<379is; oa an claspkinslo**mmeth600"> ¶,p; as $b title="2005-04-05 07pan classass="keyword">,p; as $class="stri title="2005-04-05 07pan classass="keyword">,default">class="striss="keyword">[(" '"as $word2[$word2$inserts title="2005-04-05 07pan classass="keyword">,default">$class="stri title="2005-04-05 07pan class%bsp; $word2$rnplaans title="2005-04-05 07pan classass="keyword">,default">floonpan class="default">$word1);$word2$word2$deletns title="2005-04-05 07pan classass="keyword">,default">floonpan class="default">$word1);$word2$word2eiv ss="keyword">,p; as var_exp clss="keyword">[
[
[
[
n class="string">"Did you mean:
ss="votes"> 117767>nction.lno an>n>nshtein&vote=down" title="Votqbolecvoted">down
15-08-05 08:10>
2 href="#7658" class="name"> sltz at protonic dot comre you 9:01pan cla1. multibys UTF-8fcharacommsan cla2.k$rewhich isfefek600"> ¶    * closest m>$word1);< $word1);,as }her words, ifpublicz"taspc cbe the
    * closest mpubpanpan class="default">$word1
);,,,$word2,
,$word2,,$word1);< $word1);,,,as }her words, ifpublicz"taspc cbe the
    * closest mletommsaan class="default">$word1
);,default">selftitle="2005-04-05 07pan class::s="keyword">,$word1);,$word1,$word2$bptr ,< [););$word1,[$bptr ,$word1);,$word1);,$word2,$word1< [
,$word1);,$word1);,$word1< [
    * closest mlcs_last_columspan class="default">$word1,,$word2,$word2););$word2,$word2););$bptr < [,$word2$word1,$word2strlen$bptr < [,$word2[,$word2[
,[,< $word2,default">ss="default">$word2,$word1as ,[as ,default">1 title="2005-04-05 07pan class+can>);[as ,default">maxo 'haert'. Where as, btlfsa s("keyword">,ast_rowo 'haert'. Where as, btlfsa s[p;       }
[
,[
as ,,default">$vs="default">$word2);,default">$current_rowo 'haert'. Where as, btlfsa s[p;       }
$word2,ast_row title="2005-04-05 07pan classass="keyword">,default">$current_rowo 'haert'. Where as, btlfsa saifflen< }her words, ifpublicz"taspc cbe the
    * closest mlcsaan class="default">$word1
);,,default">selftitle="2005-04-05 07pan class::s="keyword">,$word1););,default">selftitle="2005-04-05 07pan class::s="keyword">,$word1););$word2,$word2);,$word2,$word2$word2as ,$word2,[,$word2,$word2);$word2,,[$word2as ,$word2< $word2,,cs_last_columspan class="default">$word1,,[);,$word2,$word2< [);$word2,[);,cs_last_columspan class="default">$word1,[);,[);[);,$word2< [
,,default">ss="default">$word2,cs title="2005-04-05 07pan classass="keyword">,default">ss="default">$word2< $word2,default">$ccs_lef* title="2005-04-05 07pan class+can>);$word2$word1,cs title="2005-04-05 07pan classfault">$a$word2,cs title="2005-04-05 07pan classass="keyword">,default">ropthe<*="default">$word2,,default">=ipan class="default">$word1,csss="keyword">[);,$word1);,$word2,,$word1);" '",$word2,$word2< [,csss="keyword">[);,$word1);,,$word1);" '",$word2< [}s="keyw class="keyword">< ic;ig le$rsfwonsexploded into multi-bys characomms in0O(n lg n)ptimean cla2.ki lstad ofz"2003->$refor37953evmorbyespliacom379e seinndz"t">$rein0half, aid3reiursively*callcom379e algorithm twicee T;e5onlyeef>$rewe nemorfrom*amazreiursive*callfwons mazvalues in07953middleainlums. T;e trick isfeocan>$rsfwid37953g spramOk ss="votes">
<118694">Add '.'
16-01-20 01:55>
hre="#7658" class="name"> sltz at protonic dot com< ¶,p; as $b title="2005-04-05 07pan classass="keyword">,p; as $class="stri title="2005-04-05 07pan classass="keyword">,default">class="striss="keyword">[(" '"$inserts you us_len&n"date" title="2005-04-05 07pan classass="keyword">,default">class="striss="keyword">[(" '"as $word2< $word2$rnplaans_len&nb"date" title="2005-04-05 07pan classass="keyword">,default">class="striss="keyword">[(" '"$word2< as $word2$deletns you us_len&n"date" title="2005-04-05 07pan classass="keyword">,default">class="striss="keyword">[(" '"$word2< $word2< as ,p; as var_exp clss="keyword">[
$class="stri"string">" '"[
$rnplaansss="keyword">[
< class="keyword"><
8-04-17 02:42>
9 href="#7658" class="name"> sltz at protonic dot com/*e9ithssimilar_lasspan class="defspan class="default">600"> ¶    * closest m_similarss="keyword">[(as as ,$word1);as );,$word1););,,as
,[,$word2,< [
ss="votes"> 36900>nction.lno an>n>nshtein&vote=down" title="Vot ialbrainmw3->ne liksernoIHATESPAMsernotiscalisernoitvoted">down
3-10-26 11:55>
4 href="#7658" class="name"> sltz at protonic dot com600"> &pT;is; n>T;nsusedfclamula ispan claperce< class="keyword">&lnbsp;     * closest m"2003-_similarss="keyword">[(as as as $word2,,nshs title="2005-04-05 07pan classass="keyword">,default">w="de_pansaan class="default">$word1);as );nshs title="2005-04-05 07pan classa;bsp; );,default">str>$word1);as as );,default">class="striss="keyword">[(as ,as as /*..ner words, if you us_len&nb);< as ($word2,$word2($word1);[,as < $word2'ng">'&nan>);$word1);[,as < $word2,< as < $word2,$word2,default">w="de_sumss="keyword">[(as < $word2,< as < $word2,$word2< [/*ewid3false re">/*./*s30z('0.00' de&lpreiishe< isf2ewid3so on) de:/*s3false de&lreinn isfempty, bu. thnsweigh*seer words, if you us_len&nb<3w "divishe< by 0" error.ner words, if you us_len&nb< class="keyword"><
3-10-25 07:28>
4 href="#7658" class="name"> sltz at protonic dot com<3leass="stri(); n>$k it'sfwwa>$rewhole mess<3a custom*cbe the<3a;is; n>
$k 100"isfenough cor3written Eng ish (maybe oefer la/su Ger <3mkey capi ><3a threshold3of 30%. W;en excemoed,cs lowercase thnswhole mess
8-05-28 01:03>
9 href="#7658" class="name"> sltz at protonic dot com$rele< ¶< class="keyword">&lnbsp;     * closest mleass="stri2title="2005-04-05 07pan classnass="keyword">(as ,default">nulls="default">$word2,default">nulls="default">$word2,default">nulls="default">$word2,default">w="de_fills="default">$word2< $word2,default">str>$word1);as );as $word2< $word2,default">str>$word1););as $word2);,default">ss="default">$word2
);,default">keyword">as $a$word1);as );as as ,as $word1$word2,default">=ipan class="default">$word1);,default">keyword">as $a$word1););as as ,as $word2as ,default">=jeyword">as
);,default">keyword">as $a$word1);as );as as );,default">keyword">as $a$word1););as as ,default">keyword">as );as ,default">keyword">as < ,default">keyword">as
,default">ss="default">$word2,as $word1as ,default">mriss="keyword">[(as ,default">keyword">as as );as as $word1,default">keyword">as );as as ,default">keyword">as ,default">keyword">as );,,default">=deyword">as $word1as < <
2-04-11 06:57>
5 href="#7658" class="name"> sltz at protonic dot com$regpenicaspans, delay3couldrbe tolerwble if you g>$k a los ofzspell check
sectpan>'redirectphttp://fr2"> .net
anoimg src='/im add a titw cmall>00" t>ual/voce