web-dev-qa-db-fra.com

Quels sont certains algorithmes pour comparer la similitude de deux chaînes?

J'ai besoin de comparer des chaînes pour décider si elles représentent la même chose. Cela concerne les titres de cas saisis par les humains où les abréviations et autres petits détails peuvent différer. Par exemple, considérez les deux titres suivants:

std::string first = "Henry C. Harper v. The Law Offices of Huey & Luey, LLP";

Par opposition à:

std::string second = "Harper v. The Law Offices of Huey & Luey, LLP";

Un être humain peut rapidement évaluer que ce sont probablement les mêmes. L'approche que j'ai adoptée actuellement est de normaliser les chaînes en minuscules toutes les lettres et en supprimant toute ponctuation et les espaces donnant:

std::string firstNormalized = "henrycharpervthelawofficesofhueylueyllp";

Et:

std::string secondNormalized = "harpervthelawofficesofhueylueyllp";

En comparant dans ce cas, l'une est une sous-séquence de l'autre, mais vous pouvez imaginer d'autres variations plus complexes où cela ne se produit pas nécessairement, mais elles ont en commun des sous-séquences importantes. Il peut également y avoir des erreurs de saisie humaine occasionnelles telles que des lettres transposées et des fautes d'orthographe.

Peut-être qu'une sorte de programme de diff de caractères pourrait aider? J'ai vu de bons programmes de comparaison de ligne pour comparer les différences de code à enregistrer, y a-t-il quelque chose comme ça sur une base de caractères, peut-être en boost? Si vous pouviez compter le nombre de caractères consécutifs en commun et prendre le rapport aux caractères non partagés, ce serait peut-être une bonne heuristique?

En fin de compte, j'ai besoin d'une décision booléenne pour les considérer comme identiques ou non. Il ne doit pas être parfait, mais il devrait idéalement rarement se tromper.

Quel algorithme puis-je utiliser qui me donnera une sorte de quantification de la similitude des deux chaînes que je pourrai ensuite convertir en réponse oui/non au moyen d'une heuristique?

51
WilliamKF

Ce que vous recherchez s'appelle String Metric algorithmes. Il y en a significatif nombre d'entre eux, beaucoup avec des caractéristiques similaires. Parmi les plus populaires:

  • Levenshtein Distance : Le nombre minimum de modifications à un seul caractère nécessaires pour changer un mot en un autre. Les cordes ne doivent pas nécessairement avoir la même longueur
  • Distance de Hamming : Le nombre de caractères différents dans deux chaînes de longueur égale.
  • Smith – Waterman : Une famille d'algorithmes pour calculer les similitudes de sous-séquences variables.
  • Coefficient de Sørensen – Dice : Un algorithme de similitude qui calcule les coefficients de différence des paires de caractères adjacentes.

Jetez un œil à ceux-ci ainsi qu'à d'autres sur la page wiki sur le sujet.

75
Daniel Frey

Damerau Levenshtein distance est un autre algorithme pour comparer deux chaînes et il est similaire à l'algorithme de distance de Levenshtein. La différence entre les deux est qu'il peut également vérifier les transpositions entre les caractères et donc donner un meilleur résultat pour la correction d'erreur.

Par exemple: la distance de Levenshtein entre night et nigth est de 2 mais la distance de Damerau Levenshtein entre night et nigth sera de 1 car il s'agit simplement d'un échange de une paire de personnages.

10
Ankit Chaurasia

Vous pouvez utiliser des ngrams pour cela. Par exemple, transformez les deux chaînes en trigrammes Word (généralement en minuscules) et comparez le pourcentage d'entre elles qui sont égales l'une à l'autre.

Votre défi est de définir un pourcentage minimum de similitude.

http://en.wikipedia.org/wiki/N-gram

3
noderman

Un autre algorithme que vous pouvez considérer est la similitude de Simon White:

def get_bigrams(string):
    """
    Take a string and return a list of bigrams.
    """
    if string is None:
        return ""

    s = string.lower()
    return [s[i : i + 2] for i in list(range(len(s) - 1))]
def simon_similarity(str1, str2):
    """
    Perform bigram comparison between two strings
    and return a percentage match in decimal form.
    """
    pairs1 = get_bigrams(str1)
    pairs2 = get_bigrams(str2)
    union = len(pairs1) + len(pairs2)

    if union == 0 or union is None:
        return 0

    hit_count = 0
    for x in pairs1:
        for y in pairs2:
            if x == y:
                hit_count += 1
                break
    return (2.0 * hit_count) / union
2

Vous pouvez utiliser l'algorithme de calcul de la longueur de la sous-séquence commune la plus longue pour résoudre le problème. Si la longueur de la sous-séquence commune la plus longue pour les deux chaînes d'entrée est inférieure à la longueur de l'une ou l'autre des chaînes, elles sont inégales.

Vous pouvez utiliser l'approche de la programmation dynamique pour résoudre le problème et optimiser la complexité de l'espace au cas où vous ne souhaitez pas déterminer la sous-séquence commune la plus longue.

0
nmg_vikas