J'ai besoin de comparer 2 chaînes et de calculer leur similitude, pour filtrer une liste des chaînes les plus similaires.
Par exemple. la recherche de "chien" reviendrait
Par exemple. la recherche de "crack" reviendrait
J'ai rencontré:
Connaissez-vous d'autres algorithmes de similitude de chaînes?
Il semble que vous ayez besoin d'une sorte de correspondance floue. Voici Java implémentation d'un ensemble de métriques de similitude http://www.dcs.shef.ac.uk/~sam/stringmetrics.html . Voici plus explication détaillée des métriques de chaîne http://www.cs.cmu.edu/~wcohen/postscript/ijcai-ws-2003.pdf cela dépend de la façon dont votre implémentation doit être floue et rapide.
La distance Levenshtein est l'algorithme que je recommanderais. Il calcule le nombre minimum d'opérations que vous devez effectuer pour changer 1 chaîne en une autre. Le moins de changements signifie que les chaînes sont plus similaires ...
Si l'accent est mis sur les performances, j'implémenterais un algorithme basé sur une structure trie
(fonctionne bien pour trouver des mots dans un texte ou pour aider à corriger un mot, mais dans votre cas, vous pouvez trouver rapidement tous les mots contenant un mot donné ou toutes sauf une lettre, par exemple).
Veuillez d'abord suivre le lien wikipedia ci-dessus .Tries
est la méthode de tri des mots la plus rapide ( n mots, recherche s , O ( n ) pour créer le trie, O(1) pour rechercher s (ou si vous préférez, si a est la longueur moyenne, O ( an ) pour le trie et O ( s ) pour la recherche)).
Une implémentation rapide et facile (à optimiser) de votre problème (mots similaires) consiste en
Exemple, avec les mots car
, vars
.
Construire le trie (une grosse lettre signifie qu'un mot se termine ici, tandis qu'un autre peut continuer). Le >
est post-index (aller de l'avant) et <
est un pré-index (retour en arrière). Dans un autre exemple, nous devrons peut-être indiquer également la lettre de départ, elle n'est pas présentée ici pour plus de clarté.
Le <
et >
en C++ par exemple serait Mystruct *previous,*next
, signifiant de a > c < r
, vous pouvez passer directement de a
à c
, et inversement, également de a
à R
.
1. c < a < R
2. a > c < R
3. > v < r < S
4. R > a > c
5. > v < S
6. v < a < r < S
7. S > r > a > v
En cherchant strictement voiture le trie vous donne accès à partir de 1., et vous trouvez voiture (vous auriez également trouvé tout ce qui commence par voiture , mais aussi tout ce qui contient voiture à l'intérieur - ce n'est pas dans l'exemple - mais vicaire par exemple aurait été trouvé dans c > i > v < a < R
).
Pour effectuer une recherche tout en autorisant une tolérance erronée/manquante à 1 lettre, vous devez itérer à partir de chaque lettre de s , et compter le nombre de consécutives - ou en sautant 1 lettre - lettres que vous obtenez de s dans le trie.
recherche de car
,
c
: recherche du tri pour c < a
et c < r
(lettre manquante dans s ). Pour accepter une mauvaise lettre dans un mot w , essayez de sauter à chaque itération la mauvaise lettre pour voir si ar
est derrière, ceci est O ( w ). Avec deux lettres, O ( w ²) etc ... mais un autre niveau d'index pourrait être ajouté au trie pour prendre en compte le sauter sur les lettres - rendant le trie complexe et gourmand en mémoire.a
, puis r
: idem que ci-dessus, mais recherche également en arrièreC'est juste pour donner une idée du principe - l'exemple ci-dessus peut avoir quelques problèmes (je vérifierai à nouveau demain).
Vous pouvez faire ceci:
Pour chaquechaînedansmeule de foinFairedécalage : = -1; matchedCharacters : = 0; Pour chaquecarboniserdansaiguilleFairedécalage : = PositionInString (chaîne, carboniser, décalage+1); Sidécalage = -1 AlorsPause; Fin; matchedCharacters : = matchedCharacters + 1; Fin; SimatchedCharacters > 0 Alors // correspondance (partielle) trouvée Fin; Fin;
Avec matchedCharacters vous pouvez déterminer le "degré" de la correspondance. Si elle est égale à la longueur de aiguille, tous les caractères de aiguille sont également dans chaîne. Si vous stockez également l'offset du premier caractère apparié, vous pouvez également trier le résultat par la "densité" des caractères appariés en soustrayant l'offset du premier caractère apparié de l'offset du dernier caractère apparié offset; plus la différence est faible, plus la correspondance est dense.
class Program {
static int ComputeLevenshteinDistance(string source, string target) {
if ((source == null) || (target == null)) return 0;
if ((source.Length == 0) || (target.Length == 0)) return 0;
if (source == target) return source.Length;
int sourceWordCount = source.Length;
int targetWordCount = target.Length;
int[,] distance = new int[sourceWordCount + 1, targetWordCount + 1];
// Step 2
for (int i = 0; i <= sourceWordCount; distance[i, 0] = i++);
for (int j = 0; j <= targetWordCount; distance[0, j] = j++);
for (int i = 1; i <= sourceWordCount; i++) {
for (int j = 1; j <= targetWordCount; j++) {
// Step 3
int cost = (target[j - 1] == source[i - 1]) ? 0 : 1;
// Step 4
distance[i, j] = Math.Min(Math.Min(distance[i - 1, j] + 1, distance[i, j - 1] + 1), distance[i - 1, j - 1] + cost);
}
}
return distance[sourceWordCount, targetWordCount];
}
static void Main(string[] args){
Console.WriteLine(ComputeLevenshteinDistance ("Stackoverflow","StuckOverflow"));
Console.ReadKey();
}
}