web-dev-qa-db-fra.com

Comment trouver des séquences répétitives de caractères dans un tableau donné?

Mon problème est de trouver la séquence répétitive de caractères dans le tableau donné. simplement, pour identifier le motif dans lequel les caractères apparaissent.

   .---.---.---.---.---.---.---.---.---.---.---.---.---.---.
1: | J | A | M | E | S | O | N | J | A | M | E | S | O | N |
   '---'---'---'---'---'---'---'---'---'---'---'---'---'---'
   .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
2: | R | O | N | R | O | N | R | O | N | R | O | N | R | O | N |
   '---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'
   .---.---.---.---.---.---.---.---.---.---.---.---.
3: | S | H | A | M | I | L | S | H | A | M | I | L |
   '---'---'---'---'---'---'---'---'---'---'---'---'
   .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
4: | C | A | R | P | E | N | T | E | R | C | A | R | P | E | N | T | E | R |
   '---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'

Exemple

Compte tenu des données précédentes, le résultat devrait être:

  1. "JAMESON"
  2. "RON"
  3. "SHAMIL"
  4. "CARPENTER"

Question

  • Comment traiter efficacement ce problème?
33
brainless

Pour vos exemples, ma première approche serait de

  1. obtenir le premier caractère du tableau (pour votre dernier exemple, ce serait C)
  2. obtenir l'index de la prochaine apparition de ce caractère dans le tableau (par exemple 9)
  3. si elle est trouvée, recherche de la prochaine apparition de la sous-chaîne entre les deux apparences du caractère (dans ce cas, CARPENTER)
  4. si elle est trouvée, vous avez terminé (et le résultat est cette sous-chaîne).

Bien sûr, cela ne fonctionne que pour un sous-ensemble très limité de tableaux possibles, où le même mot est répété maintes et maintes fois, en commençant par le début, sans caractères parasites entre les deux, et son premier caractère n'est pas répété dans le mot. Mais tous vos exemples entrent dans cette catégorie - et je préfère la solution la plus simple qui puisse éventuellement fonctionner :-)

Si le mot répété contient plusieurs fois le premier caractère (par exemple, CACTUS), l'algorithme peut être étendu pour rechercher également les occurrences ultérieures de ce caractère, et pas seulement le premier (de sorte qu'il trouve le mot entier répété, et pas seulement une sous-chaîne de caractères). il).

Notez que cet algorithme étendu donnerait un résultat différent pour votre deuxième exemple, à savoir RONRON au lieu de RON.

18
Péter Török

La langue dans les joues O(NlogN) solution

Effectuer une FFT sur votre chaîne (traiter les caractères comme des valeurs numériques). Chaque pic du graphique obtenu correspond à une périodicité de sous-chaîne.

25

En Python, vous pouvez utiliser les regex comme suit:

def recurrence(text):
    import re
    for i in range(1, len(text)/2 + 1):
        m = re.match(r'^(.{%d})\1+$'%i, text)
        if m: return m.group(1)

recurrence('abcabc') # Returns 'abc'

Je ne sais pas comment cela se traduirait en Java ou en C. (C'est l'une des raisons pour lesquelles j'aime Python, je suppose. :-)

6
Marcelo Cantos

Commencez par écrire une méthode qui trouve la sous-chaîne répétitive sub dans la chaîne de conteneur, comme indiqué ci-dessous.

boolean findSubRepeating(String sub, String container);

Continuez maintenant à appeler cette méthode avec l'augmentation de la sous-chaîne dans le conteneur. Commencez par essayer une sous-chaîne de 1 caractère, puis 2 caractères, etc. jusqu'à container.length/2.

2
fastcodejava

La première idée qui me vient à l’esprit est d’essayer toutes les séquences répétitives de longueurs divisant la longueur (S) = N. Il existe un maximum de N/2 de telles longueurs, il en résulte un algorithme O (N ^ 2).

Mais je suis sûr que cela peut être amélioré ...

1
Eyal Schneider

Pseudocode

len = str.length
for (i in 1..len) {
   if (len%i==0) {
      if (str==str.substr(0,i).repeat(len/i)) {
         return str.substr(0,i)
      }
   }
}

Remarque: par souci de concision, j'invente une méthode de "répétition" pour les chaînes, qui ne fait pas partie de la chaîne de Java; "abc" .repeat (2) = "abcabc"

1
Erich Kitzmueller

Utilisation de C++:

//Splits the string into the fragments of given size
//Returns the set of of splitted strings avaialble
set<string> split(string s, int frag)
{
    set<string> uni;
    int len = s.length();
    for(int i = 0; i < len; i+= frag)
    {
        uni.insert(s.substr(i, frag));
    }

    return uni;
}

int main()
{

    string out;
    string s = "carpentercarpenter";
    int len = s.length();

      //Optimistic approach..hope there are only 2 repeated strings
      //If that fails, then try to break the strings with lesser number of
      //characters
    for(int i = len/2; i>1;--i)
    {
        set<string> uni = split(s,i);
        if(uni.size() == 1)
        {
            out = *uni.begin();
            break;
        }
    }

    cout<<out;
    return 0;

}
1
Asha

Mettez tous vos personnages dans un tableau e.x. une[]

i=0; j=0;
for( 0 < i < count ) 
{
if (a[i] == a[i+j+1])
    {++i;}
else
    {++j;i=0;}
}

Ensuite, le rapport de (i/j) = nombre de répétitions dans votre tableau. Vous devez faire attention aux limites de i et j, mais c'est la solution simple.

0
user2617898

et voici un exemple concret de travail:

/* find greatest repeated substring */
char *fgrs(const char *s,size_t *l)
{
  char *r=0,*a=s;
  *l=0;
  while( *a )
  {
    char *e=strrchr(a+1,*a);
    if( !e )
      break;
    do {
      size_t t=1;
      for(;&a[t]!=e && a[t]==e[t];++t);
      if( t>*l )
        *l=t,r=a;
      while( --e!=a && *e!=*a );
    } while( e!=a && *e==*a );
    ++a;
  }
  return r;
}

  size_t t;
  const char *p;
  p=fgrs("BARBARABARBARABARBARA",&t);
  while( t-- ) putchar(*p++);
  p=fgrs("0123456789",&t);
  while( t-- ) putchar(*p++);
  p=fgrs("1111",&t);
  while( t-- ) putchar(*p++);
  p=fgrs("11111",&t);
  while( t-- ) putchar(*p++);
0
user411313

Voici une solution plus générale au problème, qui trouvera des sous-séquences répétitives dans une séquence (de n'importe quoi), où les sous-séquences ne doivent pas nécessairement commencer au début, ni se suivre immédiatement.

étant donné une séquence b [0..n], contenant les données en question, et un seuil t étant la longueur minimale de sous-séquence à rechercher,

l_max = 0, i_max = 0, j_max = 0;
for (i=0; i<n-(t*2);i++) {
  for (j=i+t;j<n-t; j++) {
    l=0;
    while (i+l<j && j+l<n && b[i+l] == b[j+l])
      l++;
    if (l>t) {
      print "Sequence of length " + l + " found at " + i + " and " + j);
      if (l>l_max) {
        l_max = l;
        i_max = i;
        j_max = j;
      }
    }
  }
}
if (l_max>t) {
  print "longest common subsequence found at " + i_max + " and " + j_max + " (" + l_max + " long)";
}

Fondamentalement:

  1. Commencez au début des données, itérez jusqu'à moins de 2 * t de la fin (impossible d'avoir deux sous-séquences distinctes de longueur t sur moins de 2 * t d'espace!)
  2. Pour la deuxième sous-séquence, commencez au moins t octets au-delà du début de la première séquence.
  3. Ensuite, réinitialisez la longueur de la sous-séquence découverte à 0 et vérifiez si vous avez un caractère commun à i + l et j + l. Tant que vous le faites, augmentez l. Lorsque vous n'avez plus de caractère commun, vous avez atteint la fin de votre sous-séquence commune. Si la sous-séquence est plus longue que votre seuil, imprimez le résultat.
0
Rogan Dawes

Je viens de comprendre cela et d’écrire du code (écrit en C #) avec beaucoup de commentaires. J'espère que cela aide quelqu'un:

// Check whether the string contains a repeating sequence.
public static bool ContainsRepeatingSequence(string str)
{
    if (string.IsNullOrEmpty(str)) return false;

    for (int i=0; i<str.Length; i++)
    {
        // Every iteration, cut down the string from i to the end.
        string toCheck = str.Substring(i);

        // Set N equal to half the length of the substring. At most, we have to compare half the string to half the string. If the string length is odd, the last character will not be checked against, but it will be checked in the next iteration.
        int N = toCheck.Length / 2;

        // Check strings of all lengths from 1 to N against the subsequent string of length 1 to N.
        for (int j=1; j<=N; j++)
        {
            // Check from beginning to j-1, compare against j to j+j.
            if (toCheck.Substring(0, j) == toCheck.Substring(j, j)) return true;
        }
    }

    return false;
}

N'hésitez pas à poser des questions s'il n'est pas clair pourquoi cela fonctionne.

0
Foofnar

Vous ne savez pas comment vous définissez "efficacement". Pour une implémentation facile/rapide, vous pouvez le faire en Java:

    private static String findSequence(String text) {
        Pattern pattern = Pattern.compile("(.+?)\\1+");
        Matcher matcher = pattern.matcher(text);
        return matcher.matches() ? matcher.group(1) : null;
    }

il essaie de trouver la chaîne la plus courte (.+?) qui doit être répétée au moins une fois (\1+) pour correspondre au texte d'entrée entier.

0
Carlos Heuberger

Je voudrais convertir le tableau en objet String et utiliser regex

0
manolowar