2009-01-08 14 views
2

J'ai besoin de vérifier une chaîne pour voir si un mot contient plusieurs occurrences. Donc, fondamentalement, j'accepterai:PHP Plusieurs occurrences de mots dans une chaîne

"google fait l'amour"

mais je n'accepte pas:

"Google fait google amour" ou "Google fait l'amour l'amour google", etc.

Des idées? Je ne sais vraiment pas comment aborder cela, toute aide serait grandement appréciée.

+1

Remarque; J'ai corrigé mon code et l'ai testé. J'ai eu une petite faute de frappe – nlaq

+0

Pourriez-vous poster votre script de ma réponse que vous avez dit n'a pas fonctionné? Je l'ai testé et ça fonctionne bien. En outre, si vous voulez insensible à la casse, utilisez "! Strcasecmp ($ words [$ i], $ words [$ p])" insead du "==" – nlaq

+0

"Hello" et "Hello?" un ou deux mots? Au fond, quels délimiteurs attendez-vous? – jmucchiello

Répondre

5

Basé sur le code de puces Wicked:

function single_use_of_words($str) { 
    $words = explode(' ', trim($str)); //Trim to prevent any extra blank 
    if (count(array_unique($words)) == count($words)) { 
     return true; //Same amount of words 
    } 
    return false; 
} 
+2

Plutôt que de couper/exploser, utilisez str_word_count ($ str, 2). C'est un peu plus libéral en termes d'espace/taille. –

+0

Bien que cela fonctionne, il peut être inefficace pour les grandes chaînes; même si les premier et second mots sont identiques, array_unique visitera toujours chaque élément du tableau. Vous pouvez parcourir les éléments, en ajoutant chacun à quelque chose comme un arbre de recherche binaire, se terminant sur la première copie. –

+0

Il échoue également s'il y a deux occurrences de 2 espaces ou plus. Il y aura plusieurs zéros dans le tableau $ words qui seront comptés comme un seul mot dans l'appel array_unique. – jmucchiello

1
function Accept($str) 
{ 
    $words = explode(" ", trim($str)); 
    $len = count($words); 
    for ($i = 0; $i < $len; $i++) 
    { 
     for ($p = 0; $p < $len; $p++) 
     { 
      if ($p != $i && $words[$i] == $words[$p]) 
      { 
       return false; 
      } 
     } 
    } 
    return true; 
} 

EDIT

script test. Notez, lors de l'impression "faux" php imprime rien mais vrai est imprimé comme "1".

<?php 

    function Accept($str) 
    { 
      $words = explode(" ", trim($str)); 
      $len = count($words); 
      for ($i = 0; $i < $len; $i++) 
      { 
        for ($p = 0; $p < $len; $p++) 
        { 
          if ($p != $i && $words[$i] == $words[$p]) 
          { 
            return false; 
          } 
        } 
      } 
      return true; 
    } 

echo Accept("google makes love"), ", ", Accept("google makes google love"), ", ", 
    Accept("google makes love love google"), ", ", Accept("babe health insurance babe"); 


?> 

imprime la sortie correcte:

1, , , 
+0

merci, mais je ne peux pas le faire fonctionner :) – zuk1

+0

Maintenant, cela fonctionne. J'ai eu une faute de frappe. $ words [$ i] == $ p devrait être $ words [$ i] == $ mots [$ p] ... Je viens de le tester moi-même :) – nlaq

+0

le code ne fonctionnera pas pour les chaînes comme "bébé santé babe d'assurance " – zuk1

0

La méthode la plus simple consiste à boucle à travers chaque mot et vérifier contre tous les mots précédents pour les doublons.

+0

Pas besoin de stocker deux tableaux. Regardez mon exemple :) – nlaq

+0

Ce n'est pas tout à fait la méthode la plus simple; Jetez un oeil à ma réponse. –

+0

Bon point. Je suis trop fatigué pour répondre aux questions à cette heure. Bien sûr, vous avez facilement le sous-tableau de tous les mots précédents. Regardez juste jusqu'à l'index actuel - 1. Duh. J'ai enlevé la partie magasin de deux tableaux. –

3

Essayez ceci:

function single_use_of_words($str) { 
    $words = explode(' ', $str); 
    $words = array_unique($words); 
    return implode(' ', $words); 
} 
+0

Un peu plus lent que ma méthode. Vous devrez comparer le nombre de deux tableaux pour obtenir le résultat recherché par le PO. Cependant, je n'ai jamais entendu parler de la fonction array_unique auparavant donc merci pour cela :) – nlaq

+0

Je n'avais pas réellement vérifié la vitesse, mais cela produit un résultat simplement. ;) –

2
<?php 
$words = preg_split('\b', $string, PREG_SPLIT_NO_EMPTY); 
$wordsUnique = array_unique($words); 
if (count($words) != count($wordsUnique)) { 
    echo 'Duplicate word found!'; 
} 
?> 
+1

L'utilisation de l'expression rationnelle pour diviser les mots va ralentir encore plus loin. –

+1

Ouais, et j'ai l'impression de lire quelques-uns des messages de l'OP qu'il a l'intention d'utiliser pour rechercher toute la source de plusieurs fichiers HTML pendant le même script.Je pense qu'il serait important de regarder la performance dans cette application. – nlaq

+0

J'ai utilisé l'expression rationnelle pour séparer les limites de mots plutôt que de les séparer en espaces, cela aurait dû être clair. –

1

Cela semble assez rapide. Il serait intéressant de voir (pour toutes les réponses) comment l'utilisation de la mémoire et le temps pris augmentent à mesure que vous augmentez la longueur de la chaîne d'entrée.

function check($str) { 
    //remove double spaces 
    $c = 1; 
    while ($c) $str = str_replace(' ', ' ', $str, $c); 

    //split into array of words 
    $words = explode(' ', $str); 
    foreach ($words as $key => $word) { 
     //remove current word from array 
     unset($words[$key]); 
     //if it still exists in the array it must be duplicated 
     if (in_array($word, $words)) { 
      return false; 
     } 
    } 
    return true; 
} 

Modifier

de Correction d'un problème avec plusieurs espaces. Je ne suis pas sûr s'il est préférable de les supprimer au début (comme je l'ai fait) ou de vérifier que chaque mot est non vide dans le foreach.

+0

Je serais intéressé aussi. – nlaq

+0

L'utilisation de la mémoire ne devrait pas être trop mauvaise, mais elle a le temps O (n^2) parce que in_array est une recherche linéaire. Cela souffre également du problème de l'espace double. – jmucchiello

3

Pas besoin de boucles ou de tableaux:

<?php 

$needle = 'cat'; 
$haystack = 'cat in the cat hat'; 

if (occursMoreThanOnce($haystack, $needle)) { 
    echo 'Success'; 
} 

function occursMoreThanOnce($haystack, $needle) { 
    return strpos($haystack, $needle) !== strrpos($haystack, $needle); 
} 

?> 
2

La façon d'expression régulière serait certainement mon choix.

J'ai fait un petit test sur une chaîne de 320 mots avec la fonction de Veynom et une expression régulière

function preg($txt) { 
    return !preg_match('/\b(\w+)\b.*?\1/', $txt); 
} 

est ici le test

$time['preg'] = microtime(true); 

for($i = 0; $i < 1000; $i++) { 
    preg($txt); 
} 

$time['preg'] = microtime(true) - $time['preg']; 


$time['veynom-thewickedflea'] = microtime(true); 

for($i = 0; $i < 1000; $i++) { 
    single_use_of_words($txt); 
} 

$time['veynom-thewickedflea'] = microtime(true) - $time['veynom-thewickedflea']; 

print_r($time); 

Et voici le résultat que je suis

Array 
(
    [preg] => 0.197616815567 
    [veynom-thewickedflea] => 0.487532138824 
) 

Ce qui suggère que la solution RegExp, tout en étant beaucoup plus concis, est plus de deux fois plus vite. (Pour une chaîne de 320 mots 1000 itérations) anr

Quand je lance le test de plus de 10 000 itérations je reçois

Array 
(
    [preg] => 1.51235699654 
    [veynom-thewickedflea] => 4.99487900734 
) 

La solution non RegExp utilise aussi beaucoup plus de mémoire.

So .. expressions régulières pour moi cos ils ont un réservoir plein de gaz

EDIT
Le texte que je teste contre a des mots en double, Dans le cas contraire, les résultats peuvent être différents . Je posterai un autre ensemble de résultats.

Mise à jour
Avec les doublons dépouillé (maintenant 186 mots) les résultats pour 1000 itérations est:

Array 
(
    [preg] => 0.235826015472 
    [veynom-thewickedflea] => 0.2528860569 
) 

A propos Evens

+0

Le seul problème ici est la méthode array_unique appellera "bonjour" et "bonjour?" deux mots différents alors que preg_split en utilisant \ w + considérera ceux-ci comme égaux à "hello". Pour dupliquer la méthode array_unique dont vous avez besoin ([- \ b] +) à la place de (\ w +). Cela pourrait changer votre vitesse. – jmucchiello

+0

N'est-ce pas un autre argument pour l'approche rationnelle? bonjour et bonjour? sont le même mot sûrement. – meouw