Question d'entrevue: Vérifiez si une chaîne est une rotation d'une autre chaîne [fermé]


235

Un de mes amis s'est vu poser la question suivante aujourd'hui lors d'une entrevue pour le poste de développeur de logiciels:

Étant donné deux chaînes s1et s2comment allez-vous vérifier s'il s1existe une version pivotée de s2?

Exemple:

Si s1 = "stackoverflow"alors voici quelques-unes de ses versions tournées:

"tackoverflows"
"ackoverflowst"
"overflowstack"

où as "stackoverflwo"n'est pas une version pivotée.

La réponse qu'il a donnée était:

Prenez s2et trouvez le préfixe le plus long qui est une sous-chaîne de s1, qui vous donnera le point de rotation. Une fois que vous avez trouvé ce point, arrêtez-vous s2à ce point pour obtenir s2aet s2b, puis vérifiez simplement siconcatenate(s2a,s2b) == s1

Cela ressemble à une bonne solution pour moi et mon ami. Mais l'intervieweur a pensé le contraire. Il a demandé une solution plus simple. S'il vous plaît, aidez-moi en disant comment feriez-vous cela Java/C/C++?

Merci d'avance.


4
Vous n'avez pas à vérifier si concaténer (s2a, s2b) == s1, car vous savez que s2a est égal au début de s1. Vous pouvez simplement vérifier si s2b == la sous-chaîne de s1 de rotation_point à la fin.
Jason Hall

33
Comment ces questions et la meilleure réponse ont-elles obtenu autant de votes positifs?
David Johnstone

9
@David: Parce que c'est intéressant.
Cam

6
Je dirais, très intéressant et une réponse élégante et simple.
Guru

7
@David: parce que c'est une question qui n'est pas posée avant ici et aussi que tout le monde comprend (si on ne comprend pas la question / réponse, on est sûr de ne pas le voter; une question assez simple a un public plus large) et aussi parce que cela a été tagué avec Java et C. Cela compte :)
BalusC

Réponses:


687

Assurez s1- vous d'abord et s2sont de la même longueur. Ensuite, vérifiez si s2une sous-chaîne est s1concaténée avec s1:

algorithm checkRotation(string s1, string s2) 
  if( len(s1) != len(s2))
    return false
  if( substring(s2,concat(s1,s1))
    return true
  return false
end

En Java:

boolean isRotation(String s1,String s2) {
    return (s1.length() == s2.length()) && ((s1+s1).indexOf(s2) != -1);
}

49
J'aime son élégance, mais j'ai dû réfléchir un moment pour vérifier qu'il n'y avait pas de faux positifs. (Je ne pense pas qu'il y en ait.)
Jon Skeet

6
Vous pouvez également utiliser (s1+s1).contains(s2)en Java.
Lubrifiants polygéniques

4
Quoi qu'il en soit, je m'y opposerais un peu comme une question d'entrevue. Il a un "aha!" composante, je pense. La plupart des programmeurs (moi y compris) utiliseraient simplement la force brute, ce qui n'est pas déraisonnable de toute façon, et cela pourrait ne pas sembler assez «intelligent» pour l'intervieweur.
Daniel Daranas

5
@Jon Concentrate on s1+s1. De toute évidence, toutes ses sous-chaînes de taille s1.lengthsont des rotations de s1, par construction. Par conséquent, toute chaîne de taille s1.lengthqui est une sous-chaîne de s1+s1doit être une rotation de s1.
Daniel C.Sobral

6
@unicornaddict - ce qui est génial avec cette solution, c'est qu'elle est si évidente une fois que vous la signalez, je me déteste de ne pas y avoir pensé!
James B

101

Une meilleure réponse serait sûrement: "Eh bien, je demanderais à la communauté stackoverflow et j'aurais probablement au moins 4 très bonnes réponses en 5 minutes". Les cerveaux sont bons et tout, mais j'accorderais une valeur plus élevée à quelqu'un qui sait travailler avec les autres pour obtenir une solution.


14
+1 pour la joue pure. Made my day :-)
Platinum Azure

5
S'ils n'étaient pas d'accord, vous pouvez ensuite les lier à cette question.
Cam

51
Fouetter votre téléphone portable lors d'une interview pourrait être considéré comme impoli et à la fin, ils finiraient par embaucher Jon Skeet.
tstenner

2
C'est en fait probablement exactement ce que j'aurais dit
Chris Dutrow

6
Je ne pense pas qu'ils pourront se permettre Jon Skeet.
SolutionYogi

49

Un autre exemple de python (basé sur LA réponse):

def isrotation(s1,s2):
     return len(s1)==len(s2) and s1 in 2*s2

1
Fait intéressant, j'ai pensé à dupliquer s2plutôt qu'à s1trop ... puis j'ai réalisé que la relation était symétrique de toute façon.
Matthieu M.

1
Si la chaîne peut être longue, voici une version Python qui utilise Boyer-Moore pour obtenir le temps d'exécution O (n): def isrotation (s1, s2): return len (s1) == len (s2) et re.compile (re .escape (s1)). search (2 * s2) n'est pas None
Duncan

2
@Duncan: l' inopérateur n'utilise-t-il pas un algorithme O (n)?
Ken Bloom

1
@Duncan: Les méthodes de chaîne de python utilisent un Boyer-Moore-Horspool optimisé. Je me demande si Java a des optimisations similaires.
Thomas Ahle

1
@Thomas merci de l'avoir signalé. J'avais pensé que seules les expressions régulières utilisaient Boyer-Moore mais je vois que j'avais tort. Pour Python 2.4 et versions antérieures, ma réponse était correcte, mais puisque Python 2.5 s1 in s2est optimisé. Voir effbot.org/zone/stringlib.htm pour la description de l'algorithme. Google semble indiquer que Java n'a pas de recherche de chaîne rapide (voir johannburkard.de/software/stringsearch par exemple) bien que je doute que cela casserait quoi que ce soit s'ils le changeaient.
Duncan

32

Comme d'autres ont soumis une solution de complexité temporelle quadratique dans le pire des cas, j'ajouterais une solution linéaire (basée sur l' algorithme KMP ):

bool is_rotation(const string& str1, const string& str2)
{
  if(str1.size()!=str2.size())
    return false;

  vector<size_t> prefixes(str1.size(), 0);
  for(size_t i=1, j=0; i<str1.size(); i++) {
    while(j>0 && str1[i]!=str1[j])
      j=prefixes[j-1];
    if(str1[i]==str1[j]) j++;
    prefixes[i]=j;
  }

  size_t i=0, j=0;
  for(; i<str2.size(); i++) {
    while(j>0 && str2[i]!=str1[j])
      j=prefixes[j-1];
    if(str2[i]==str1[j]) j++;
  }
  for(i=0; i<str2.size(); i++) {
    if(j>=str1.size()) return true;
    while(j>0 && str2[i]!=str1[j])
      j=prefixes[j-1];
    if(str2[i]==str1[j]) j++;
  }

  return false;
}

exemple de travail


5
+1 pour ideone.com - cela semble très intéressant!
Martin Vseticka

25

EDIT: La réponse acceptée est clairement plus élégante et efficace que cela, si vous la repérez. J'ai laissé cette réponse comme ce que je ferais si je n'avais pas pensé à doubler la chaîne d'origine.


Je le ferais simplement par force brute. Vérifiez d'abord la longueur, puis essayez tous les décalages de rotation possibles. Si aucun ne fonctionne, retournez false - si l'un d'eux fonctionne, retournez true immédiatement.

Il n'y a pas besoin de concaténer en particulier - utilisez simplement des pointeurs (C) ou des index (Java) et parcourez les deux, un dans chaque chaîne - à partir du début d'une chaîne et du décalage de rotation candidat actuel dans la deuxième chaîne, et encapsuler si nécessaire . Vérifiez l'égalité des caractères à chaque point de la chaîne. Si vous arrivez à la fin de la première chaîne, vous avez terminé.

Ce serait probablement aussi facile à concaténer - bien que probablement moins efficace, au moins en Java.


8
+1 - nous n'avons pas besoin de solutions élégantes qui fonctionnent 3 fois plus que la solution la plus efficace. C'est C ... la micro-optimisation est plus rigoureuse .
Stephen C

8
Interviewer: Je dois parler, mais je parie que ce type ne peut pas coder.
Humphrey Bogart

8
@Beau: Si quelqu'un veut penser cela, il est le bienvenu de me demander le code. Si quelqu'un me demande simplement «comment je ferais quelque chose», je décris généralement l'algorithme plutôt que de sauter au code.
Jon Skeet

3
@Jon - J'ai lu le commentaire de Beau comme une blague
oxbow_lakes

37
@Jon C'était une blague! L'intervieweur n'interviewe pas Jon Skeet, Jon Skeet l'interviewe.
Humphrey Bogart

17

En voici une utilisant regex juste pour le plaisir:

boolean isRotation(String s1, String s2) {
   return (s1.length() == s2.length()) && (s1 + s2).matches("(.*)(.*)\\2\\1");
}

Vous pouvez le rendre un peu plus simple si vous pouvez utiliser un caractère de délimiteur spécial garanti de ne pas être dans l'une ou l'autre des chaînes.

boolean isRotation(String s1, String s2) {
   // neither string can contain "="
   return (s1 + "=" + s2).matches("(.*)(.*)=\\2\\1");
}

Vous pouvez également utiliser lookbehind avec une répétition finie à la place:

boolean isRotation(String s1, String s2) {
   return (s1 + s2).matches(
      String.format("(.*)(.*)(?<=^.{%d})\\2\\1", s1.length())
   );
}

6
+1 pour être un maître regex.
Chris Thornton

-1 Pour avoir mis les mots «regex» et «fun» dans la même déclaration, sans modifier «fun» par «not» (je plaisante, je n'ai pas rejeté le vote)
Binary Worrier

-3 pour avoir laissé entendre que les expressions rationnelles ne sont pas amusantes.
manlycode du

n'importe quel corps peut-il expliquer comment cette expression régulière "(. *) (. *) = \\ 2 \\ 1" a fonctionné!
mawia

10

Whoa, whoa ... pourquoi tout le monde est si ravi d'une O(n^2)réponse? Je suis certain que nous pouvons faire mieux ici. LA réponse ci-dessus inclut une O(n)opération en O(n)boucle (l'appel substring / indexOf). Même avec un algorithme de recherche plus efficace; dire Boyer-Mooreou KMP, le pire des cas est toujours O(n^2)avec des doublons.

Une O(n)réponse aléatoire est simple; prendre un hachage (comme une empreinte digitale Rabin) qui prend en charge une O(1)fenêtre coulissante; chaîne de hachage 1, puis chaîne de hachage 2, puis déplacez la fenêtre de hachage 1 autour de la chaîne et voyez si les fonctions de hachage entrent en collision.

Si nous imaginons que le pire des cas est quelque chose comme "scanner deux brins d'ADN", alors la probabilité de collisions augmente, et cela dégénère probablement en quelque chose comme O(n^(1+e))ou quelque chose (deviner juste ici).

Enfin, il existe une O(nlogn)solution déterministe qui a une très grande constante à l'extérieur. Fondamentalement, l'idée est de prendre une convolution des deux cordes. La valeur maximale de la convolution sera la différence de rotation (si elles sont tournées); un O(n)chèque confirme. La bonne chose est que s'il y a deux valeurs maximales égales, elles sont toutes les deux également des solutions valides. Vous pouvez faire la convolution avec deux FFT et un produit scalaire, et un iFFT, donc nlogn + nlogn + n + nlogn + n == O(nlogn).

Étant donné que vous ne pouvez pas remplir de zéros et que vous ne pouvez pas garantir que les chaînes ont une longueur de 2 ^ n, les FFT ne seront pas les plus rapides; ce seront les lents, O(nlogn)mais toujours une constante beaucoup plus grande que l'algorithme CT.

Tout cela dit, je suis absolument, 100% positif qu'il existe une O(n)solution déterministe ici, mais sacrément si je peux la trouver.


KMP sur la chaîne concaténée avec lui-même (physiquement ou virtuellement avec un %stringsize) est garanti comme étant un temps linéaire.
Kragen Javier Sitaker

+1 pour Rabin-Karp. Contrairement à KMP, il utilise un espace constant et est plus simple à mettre en œuvre. (C'est aussi la première réponse à laquelle j'ai pensé, en quelques secondes, ce qui rend difficile de voir la `` bonne '' réponse, car celle-ci est juste là et elle est douce.) Votre idée de convolution me rappelle l'algorithme de Shor - je me demande s'il y a un sublinéaire solution quantique - mais cela devient idiot maintenant, non?
Darius Bacon

1
RK ne donne pas de solution déterministe O (n) et KMP est O (n) dans l'espace, ce qui pourrait être indésirable. Recherchez la recherche de sous-chaînes bidirectionnelles ou SMOA qui sont à la fois O (n) dans le temps et O (1) dans l'espace. Soit dit en passant, glibc strstr utilise Two Way, mais si vous concaténez réellement des chaînes pour l'utiliser plutôt que d'utiliser% len, vous revenez à O (n) dans l'espace. :-)
R .. GitHub ARRÊTEZ D'AIDER LA GLACE

8

Fist, assurez-vous que les 2 cordes ont la même longueur. Ensuite, en C, vous pouvez le faire avec une simple itération de pointeur.


int is_rotation(char* s1, char* s2)
{
  char *tmp1;
  char *tmp2;
  char *ref2;

  assert(s1 && s2);
  if ((s1 == s2) || (strcmp(s1, s2) == 0))
    return (1);
  if (strlen(s1) != strlen(s2))
    return (0);

  while (*s2)
    {
      tmp1 = s1;
      if ((ref2 = strchr(s2, *s1)) == NULL)
        return (0);
      tmp2 = ref2;
      while (*tmp1 && (*tmp1 == *tmp2))
        {
          ++tmp1;
          ++tmp2;
          if (*tmp2 == '\0')
            tmp2 = s2;
        }
      if (*tmp1 == '\0')
        return (1);
      else
        ++s2;
    }
  return (0);
}

19
Ah, C. Pourquoi faire quelque chose en deux fois moins de temps et coder quand vous pouvez le faire en C!
Humphrey Bogart

11
+1 C'est très bien écrit C. Et pour être honnête, la question est étiquetée «c».
Nick Moore

5
Dans ce code, vous avez parcouru les chaînes au moins 2 sinon 3 fois (en strlen et strcmp). Vous pouvez vous enregistrer cette vérification et vous pouvez garder cette logique dans votre boucle. Pendant que vous bouclez, si un nombre de caractères de chaîne est différent de l'autre, quittez la boucle. Vous connaîtrez les longueurs, comme vous connaissez le début et vous savez quand vous avez atteint le terminateur nul.
Nasko

12
@Beau Martinez - parce que parfois le temps d'exécution est plus important que le temps de développement :-)
phkahler

2
@phkahler - Le truc c'est que ça pourrait être plus lent. Les fonctions d'index intégrées dans les autres langues utilisent généralement un algorithme de recherche de chaînes rapide comme Boyer-Moore, Rabin-Karp ou Knuth-Morris-Pratt. C'est trop naïf, réinventez tout en C et supposez que ce soit plus rapide.
Thomas Ahle

8

Voici un O(n)algorithme en place. Il utilise l' <opérateur pour les éléments des chaînes. Ce n'est pas le mien bien sûr. Je l'ai pris d' ici (le site est en polonais. Je suis tombé dessus une fois dans le passé et je n'ai pas trouvé quelque chose comme ça maintenant en anglais, donc je montre ce que j'ai :)).

bool equiv_cyc(const string &u, const string &v)
{
    int n = u.length(), i = -1, j = -1, k;
    if (n != v.length()) return false;

    while( i<n-1 && j<n-1 )
    {
        k = 1;
        while(k<=n && u[(i+k)%n]==v[(j+k)%n]) k++;
        if (k>n) return true;
        if (u[(i+k)%n] > v[(j+k)%n]) i += k; else j += k;
    }
    return false;
}

1 ... O (n) est juste sooooo beaucoup plus profond d'une -science - maquette point de vue que tous les non O (n) solution :)
SyntaxT3rr0r

4
+1 pour une solution optimale dans le temps et presque optimale en taille de code (binaire et LoC). Cette réponse serait encore meilleure avec une explication.
R .. GitHub STOP HELPING ICE

Tout à fait déroutant. Nous avons besoin d'une explication!
j_random_hacker

7

Je suppose que c'est mieux de le faire dans Java:

boolean isRotation(String s1,String s2) {
    return (s1.length() == s2.length()) && (s1+s1).contains(s2);
}

En Perl je ferais:

sub isRotation {
 my($string1,$string2) = @_;
 return length($string1) == length($string2) && ($string1.$string1)=~/$string2/;
}

ou encore mieux en utilisant la fonction d' index au lieu de l'expression régulière:

sub isRotation {
 my($string1,$string2) = @_;
 return length($string1) == length($string2) && index($string2,$string1.$string1) != -1;
}

1
Vous avez oublié \Qdans /\Q$string2/.
Kragen Javier Sitaker

3
\Qcite tous les caractères spéciaux dans $string2. Sans elle, .serait considérée comme une rotation de n'importe quelle chaîne de 1 caractère.
jackrabbit

6

Je ne sais pas si c'est la méthode la plus efficace, mais elle pourrait être relativement intéressante : la transformation Burrows-Wheeler . Selon l'article WP, toutes les rotations de l'entrée produisent la même sortie. Pour des applications telles que la compression ce n'est pas souhaitable, donc la rotation d'origine est indiquée (par exemple par un index; voir l'article). Mais pour une comparaison simple indépendante de la rotation, cela semble idéal. Bien sûr, ce n'est pas nécessairement idéalement efficace!


Étant donné que la transformation Burrows-Wheeler implique le calcul de toutes les rotations de la chaîne, elle ne sera certainement pas optimale .. :-)
R .. GitHub STOP HELPING ICE

6

Prenez chaque personnage comme une amplitude et effectuez une transformation de Fourier discrète sur eux. S'ils ne diffèrent que par la rotation, les spectres de fréquence seront identiques à l'erreur d'arrondi près. Bien sûr, cela est inefficace, sauf si la longueur est une puissance de 2, vous pouvez donc faire une FFT :-)


Nous l'avons utilisé comme un exercice de codage intéressant, je ne suis pas sûr que nous serions en mesure d'évaluer cela;).
jayshao

FFT abusé :) +1 de ma part
Aamir

5

Personne n'a encore proposé une approche modulo, alors en voici une:

static void Main(string[] args)
{
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "ztackoverflow"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "ackoverflowst"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "overflowstack"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "stackoverflwo"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "tackoverflwos"));
    Console.ReadLine();
}

public static bool IsRotation(string a, string b)
{
    Console.WriteLine("\nA: {0} B: {1}", a, b);

    if (b.Length != a.Length)
        return false;

    int ndx = a.IndexOf(b[0]);
    bool isRotation = true;
    Console.WriteLine("Ndx: {0}", ndx);
    if (ndx == -1) return false;
    for (int i = 0; i < b.Length; ++i)
    {
        int rotatedNdx = (i + ndx) % b.Length;
        char rotatedA = a[rotatedNdx];

        Console.WriteLine( "B: {0} A[{1}]: {2}", b[i], rotatedNdx, rotatedA );

        if (b[i] != rotatedA)
        {
            isRotation = false;
            // break; uncomment this when you remove the Console.WriteLine
        }
    }
    return isRotation;
}

Production:

A: stackoverflow B: ztackoverflow
Ndx: -1
Rotation : False

A: stackoverflow B: ackoverflowst
Ndx: 2
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
Rotation : True

A: stackoverflow B: overflowstack
Ndx: 5
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
Rotation : True

A: stackoverflow B: stackoverflwo
Ndx: 0
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
B: o A[12]: w
Rotation : False

A: stackoverflow B: tackoverflwos
Ndx: 1
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
B: o A[12]: w
B: s A[0]: s
Rotation : False

[EDIT: 2010-04-12]

piotr a remarqué la faille dans mon code ci-dessus. Il génère des erreurs lorsque le premier caractère de la chaîne se produit deux fois ou plus. Par exemple, le stackoverflowtest a owstackoverflowabouti à faux, alors que cela devrait être vrai.

Merci piotr d'avoir repéré l'erreur.

Maintenant, voici le code corrigé:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace TestRotate
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ztackoverflow"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ackoverflowst"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "overflowstack"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "stackoverflwo"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "tackoverflwos"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "owstackoverfl"));

            Console.ReadLine();
        }

        public static bool IsRotation(string a, string b)
        {
            Console.WriteLine("\nA: {0} B: {1}", a, b);

            if (b.Length != a.Length)
                return false;

            if (a.IndexOf(b[0]) == -1 )
                return false;

            foreach (int ndx in IndexList(a, b[0]))
            {
                bool isRotation = true;

                Console.WriteLine("Ndx: {0}", ndx);

                for (int i = 0; i < b.Length; ++i)
                {
                    int rotatedNdx = (i + ndx) % b.Length;
                    char rotatedA = a[rotatedNdx];

                    Console.WriteLine("B: {0} A[{1}]: {2}", b[i], rotatedNdx, rotatedA);

                    if (b[i] != rotatedA)
                    {
                        isRotation = false;
                        break;
                    }
                }
                if (isRotation)
                    return true;
            }
            return false;
        }

        public static IEnumerable<int> IndexList(string src, char c)
        {
            for (int i = 0; i < src.Length; ++i)
                if (src[i] == c)
                    yield return i;
        }

    }//class Program
}//namespace TestRotate

Voici la sortie:

A: stackoverflow B: ztackoverflow
Rotation : False

A: stackoverflow B: ackoverflowst
Ndx: 2
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
Rotation : True

A: stackoverflow B: overflowstack
Ndx: 5
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
Rotation : True

A: stackoverflow B: stackoverflwo
Ndx: 0
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
Rotation : False

A: stackoverflow B: tackoverflwos
Ndx: 1
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
Rotation : False

A: stackoverflow B: owstackoverfl
Ndx: 5
B: o A[5]: o
B: w A[6]: v
Ndx: 11
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
Rotation : True

Voici l'approche lambda:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IsRotation
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ztackoverflow"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ackoverflowst"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "overflowstack"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "stackoverflwo"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "owstackoverfl"));

            string strToTestFrom = "stackoverflow";
            foreach(string s in StringRotations(strToTestFrom))
            {
                Console.WriteLine("is {0} rotation of {1} ? {2}",
                    s, strToTestFrom,
                    IsRotation(strToTestFrom, s) );
            }
            Console.ReadLine();
        }

        public static IEnumerable<string> StringRotations(string src)
        {
            for (int i = 0; i < src.Length; ++i)
            {
                var sb = new StringBuilder();
                for (int x = 0; x < src.Length; ++x)
                    sb.Append(src[(i + x) % src.Length]);

                yield return sb.ToString();
            }
        }

        public static bool IsRotation(string a, string b)
        {
            if (b.Length != a.Length || a.IndexOf(b[0]) < 0 ) return false;
            foreach(int ndx in IndexList(a, b[0]))
            {
                int i = ndx;
                if (b.ToCharArray().All(x => x == a[i++ % a.Length]))
                    return true;
            }
            return false;
        }

        public static IEnumerable<int> IndexList(string src, char c)
        {
            for (int i = 0; i < src.Length; ++i)
                if (src[i] == c)
                    yield return i;
        }

    }//class Program

}//namespace IsRotation

Voici la sortie de l'approche lambda:

Rotation : False
Rotation : True
Rotation : True
Rotation : False
Rotation : True
is stackoverflow rotation of stackoverflow ? True
is tackoverflows rotation of stackoverflow ? True
is ackoverflowst rotation of stackoverflow ? True
is ckoverflowsta rotation of stackoverflow ? True
is koverflowstac rotation of stackoverflow ? True
is overflowstack rotation of stackoverflow ? True
is verflowstacko rotation of stackoverflow ? True
is erflowstackov rotation of stackoverflow ? True
is rflowstackove rotation of stackoverflow ? True
is flowstackover rotation of stackoverflow ? True
is lowstackoverf rotation of stackoverflow ? True
is owstackoverfl rotation of stackoverflow ? True
is wstackoverflo rotation of stackoverflow ? True

Je ne pense pas que votre réponse soit correcte puisque int ndx = a.IndexOf (b [0]); ne fonctionnera que s'il n'y a pas d'autres éléments avec la même valeur de b [0] dans la chaîne.
piotr

merci d'avoir remarqué la faille. corrigé maintenant
Michael Buen

3

Comme personne n'a donné de solution C ++. le voici:

bool isRotation(string s1,string s2) {

  string temp = s1;
  temp += s1;
  return (s1.length() == s2.length()) && (temp.find(s2) != string::npos);
}

Quelques points: vous effectuez la concaténation de chaîne relativement coûteuse même si les longueurs ne correspondent pas; vous pouvez passer s2 par référence const.
Tony Delroy

2

L'astuce simple de rotation du pointeur d'Opera fonctionne, mais elle est extrêmement inefficace dans le pire des cas en temps d'exécution. Imaginez simplement une chaîne avec de nombreuses séries répétitives de caractères, c'est-à-dire:

S1 = HELLOHELLOHELLO1HELLOHELLOHELLO2

S2 = HELLOHELLOHELLO2HELLOHELLOHELLO1

La "boucle jusqu'à ce qu'il y ait un décalage, puis incrémenter d'un et réessayer" est une approche horrible, sur le plan du calcul.

Pour prouver que vous pouvez faire l'approche de concaténation en C simple sans trop d'effort, voici ma solution:

  int isRotation(const char* s1, const char* s2) {
        assert(s1 && s2);

        size_t s1Len = strlen(s1);

        if (s1Len != strlen(s2)) return 0;

        char s1SelfConcat[ 2 * s1Len + 1 ];

        sprintf(s1SelfConcat, "%s%s", s1, s1);   

        return (strstr(s1SelfConcat, s2) ? 1 : 0);
}

Ceci est linéaire dans le temps d'exécution, au détriment de l'utilisation de la mémoire O (n) en surcharge.

(Notez que l'implémentation de strstr () est spécifique à la plate-forme, mais si elle est particulièrement mortelle, elle peut toujours être remplacée par une alternative plus rapide telle que l'algorithme de Boyer-Moore)


1
Connaissez-vous une plate-forme qui a strstr()en O (n + m)? De plus, si la norme (ou autre chose) ne vous garantit pas un temps d'exécution linéaire de strstr(), vous ne pouvez pas affirmer que l'algorithme entier a une compexité temporelle linéaire.
jpalecek

C'est pourquoi j'ai dit qu'il pouvait être remplacé par l'algorithme de Boyer-Moore, le faisant fonctionner en temps linéaire.
RarrRarrRarr

Il y a quelques problèmes potentiels avec votre méthode d'allocation s1SelfConcat: ce n'est que depuis C9x que C autorise les tailles de tableau variables (bien que GCC l'a autorisé beaucoup plus longtemps), et vous aurez des problèmes pour allouer de grandes chaînes sur la pile. Yosef Kreinin a écrit un article de blog très amusant sur ce problème. De plus, votre solution est toujours en quadratique avec Boyer-Moore; vous voulez KMP.
Kragen Javier Sitaker


2

J'aime LA réponse qui vérifie si s2 est une sous-chaîne de s1 concaténée avec s1.

Je voulais ajouter une optimisation qui ne perd pas son élégance.

Au lieu de concaténer les chaînes, vous pouvez utiliser une vue de jointure (je ne sais pas pour un autre langage, mais pour C ++ Boost.Range fournir ce type de vues).

Comme la vérification si une chaîne est une sous-chaîne d'une autre a une complexité moyenne linéaire (la pire des situations est quadratique), cette optimisation devrait améliorer la vitesse d'un facteur 2 en moyenne.


2

Une réponse Java pure (contrôles sans null)

private boolean isRotation(String s1,String s2){
    if(s1.length() != s2.length()) return false;
    for(int i=0; i < s1.length()-1; i++){
        s1 = new StringBuilder(s1.substring(1)).append(s1.charAt(0)).toString();
        //--or-- s1 = s1.substring(1) + s1.charAt(0)
        if(s1.equals(s2)) return true;
    }
    return false;
}

2

Et maintenant pour quelque chose de complètement différent.

Si vous voulez une réponse très rapide dans un contexte contraint lorsque les chaînes ne sont pas en rotation

  • calculer une somme de contrôle basée sur les caractères (comme xorer tous les caractères) sur les deux chaînes. Si les signatures diffèrent, les chaînes ne sont pas des rotations les unes des autres.

D'accord, cela peut échouer, mais il est très rapide de dire si les chaînes ne correspondent pas et si elles correspondent, vous pouvez toujours utiliser un autre algorithme comme la concaténation de chaînes pour vérifier.


1

Une autre solution Ruby basée sur la réponse:

def rotation?(a, b); a.size == b.size and (b*2)[a]; end

1

Il est très facile d'écrire en PHP à l'aide des fonctions strlenet strpos:

function isRotation($string1, $string2) {
    return strlen($string1) == strlen($string2) && (($string1.$string1).strpos($string2) != -1);
}

Je ne sais pas ce qui strposutilise en interne, mais s'il utilise KMP, ce sera linéaire dans le temps.


1

Inversez l'une des chaînes. Prenez la FFT des deux (en les traitant comme de simples séquences d'entiers). Multipliez les résultats ensemble par points. Transformez en utilisant la FFT inverse. Le résultat aura un seul pic si les cordes sont des rotations les unes des autres - la position du pic indiquera de combien elles sont tournées les unes par rapport aux autres.


0

Pourquoi pas quelque chose comme ça?


//is q a rotation of p?
bool isRotation(string p, string q) {
    string table = q + q;    
    return table.IndexOf(p) != -1;
}

Bien sûr, vous pouvez écrire votre propre fonction IndexOf (); Je ne sais pas si .NET utilise une méthode naïve ou une méthode plus rapide.

Naïve:


int IndexOf(string s) {
    for (int i = 0; i < this.Length - s.Length; i++)
        if (this.Substring(i, s.Length) == s) return i;
    return -1;
}

Plus rapide:


int IndexOf(string s) {
    int count = 0;
    for (int i = 0; i < this.Length; i++) {
        if (this[i] == s[count])
            count++;
        else
            count = 0;
        if (count == s.Length)
            return i - s.Length;
    }
    return -1;
}

Edit: je pourrais avoir des problèmes ponctuels; n'a pas envie de vérifier. ;)


0

Je ferais cela en Perl :

sub isRotation { 
     return length $_[0] == length $_[1] and index($_[1],$_[0],$_[0]) != -1; 
}

0
int rotation(char *s1,char *s2)
{
    int i,j,k,p=0,n;
    n=strlen(s1);
    k=strlen(s2);
    if (n!=k)
        return 0;
    for (i=0;i<n;i++)
    {
        if (s1[0]==s2[i])
        {
            for (j=i,k=0;k<n;k++,j++)
            {
                if (s1[k]==s2[j])
                    p++;
                if (j==n-1)
                    j=0;
            }
        }
    }
    if (n==p+1)
      return 1;
    else
      return 0;
}

0

Inscrivez - vous string1avec string2et utiliser l' algorithme KMP pour vérifier si string2est présent dans la chaîne nouvellement formée. Parce que la complexité temporelle de KMP est moindre que substr.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.