2009-09-04 6 views
6

La question est compliquée mais je vais l'expliquer en détails.Comment faire l'étape suivante d'une chaîne. C#

Le but est de faire une fonction qui retournera la prochaine "étape" de la chaîne donnée.

Par exemple

String.Step("a"); // = "b" 
String.Step("b"); // = "c" 
String.Step("g"); // = "h" 
String.Step("z"); // = "A" 
String.Step("A"); // = "B" 
String.Step("B"); // = "C" 
String.Step("G"); // = "H" 

Jusqu'à ici son assez facile, mais en prenant à l'esprit que l'entrée est une chaîne peut contenir plus de 1 caractères et la fonction doit se comporter comme celui-ci.

String.Step("Z"); // = "aa"; 
String.Step("aa"); // = "ab"; 
String.Step("ag"); // = "ah"; 
String.Step("az"); // = "aA"; 
String.Step("aA"); // = "aB"; 
String.Step("aZ"); // = "ba"; 
String.Step("ZZ"); // = "aaa"; 

et ainsi de suite ...

Cela ne doit pas exactement d'étendre la base de la classe String.

J'ai essayé de travailler sur les valeurs ASCII de chaque caractère, mais je me suis retrouvé coincé avec des chaînes contenant 2 caractères.

J'apprécierais vraiment si quelqu'un peut fournir le code complet de la fonction.

Merci d'avance.

EDIT * Je suis désolé, j'oublié de mentionner plus tôt que la fonction « reparse » la chaîne auto générée lorsque sa longueur atteint n. Je suis désolé je ne l'ai pas mentionné plus tôt, après avoir lu quelques réponses j'ai réalisé que le problème était en question.

Sans cela, la fonction produira toujours le caractère "a" n fois après la fin de l'étape.

+4

Pourquoi ne pas poster ce que vous avez essayé? –

+0

L'entrée est toujours a-zA-Z – George

+0

Je suis désolé j'ai oublié d'ajouter la question principale: S. Question éditée. – George

Répondre

11

REMARQUE: Cette réponse est incorrecte , comme "aa" devrait suivre après "Z" ... (voir les commentaires ci-dessous)

Voici un algorithme qui pourrait fonctionner:

chaque "chaîne" représente un nombre à une base donnée (ici: deux fois le nombre de lettres dans l'alphabet). L'étape suivante peut donc être calculée en analysant la chaîne "number" dans un int, en ajoutant 1 et en la reformatant à la base.

Exemple:

"a" == 1 -> step("a") == step(1) == 1 + 1 == 2 == "b" 

Maintenant, votre problème est réduit à la chaîne comme l'analyse un numéro à une base donnée et reformater. Un googling rapide suggère cette page: http://everything2.com/title/convert+any+number+to+decimal

Comment implémenter ceci?

  • une table de correspondance pour les lettres au nombre correspondant: a = 1, b = 2, c = 3, ... Y =?, Z = 0
  • pour analyser une chaîne à nombre, lire les caractères dans l'ordre inverse, en regardant les chiffres et les additionner:
    • "ab" -> 2 * BASE^0 + 1 * BASE^1
    • avec BASE étant le nombre de "chiffres" (2 count de lettres dans l'alphabet, est que 48?)

EDIT: Ce lien est encore plus prometteur: http://www.citidel.org/bitstream/10117/20/12/convexp.html

+0

Pourriez-vous s'il vous plaît expliquer plus brièvement, comme l'affichage d'un pseudo code ou, si possible, un plein. Merci – George

+1

Ce n'est pas si simple, quelle lettre représente 0, a? Quelle est la valeur int de "a" et quelle est la valeur int de "aa"? – AnthonyWJones

+0

@Anthony: D'après les exemples de la question, je pense que c'est simplement inversé Base52. 'a = 0, A = 26, aa = 52' – dtb

0

Divisez la chaîne d'entrée en colonnes et traitez chacune d'elles, de droite à gauche, comme vous le feriez si c'était de l'arithmétique basique. Appliquez le code que vous avez qui fonctionne avec une seule colonne à chaque colonne. Lorsque vous obtenez un Z, vous "incrémentez" la colonne suivante à gauche en utilisant le même algorithme. S'il n'y a pas de colonne next-left, collez un 'a'.

-1

LetterToNum doit être une fonction qui mappe "a" à 0 et "Z" à 51. NumToLetter l'inverse.

long x = "aazeiZa".Aggregate((x,y) => (x*52) + LetterToNum(y)) + 1; 
string s = ""; 

do { // assertion: x > 0 
    var c = x % 52; 
    s = NumToLetter() + s; 
    x = (x - c)/52; 
} while (x > 0) 

// s now should contain the result 
+0

Encore une approche basée sur Base52, cela ne fonctionne pas.La première ligne retournera 1 pour "un" ou "aa" ou "aaa" – AnthonyWJones

+0

En effet, raté le 52. –

3
public static class StringStep 
{ 
    public static string Next(string str) 
    { 
     string result = String.Empty; 
     int index = str.Length - 1; 
     bool carry; 
     do 
     { 
      result = Increment(str[index--], out carry) + result;    
     } 
     while (carry && index >= 0); 
     if (index >= 0) result = str.Substring(0, index+1) + result; 
     if (carry) result = "a" + result; 
     return result; 
    } 

    private static char Increment(char value, out bool carry) 
    { 
     carry = false; 
     if (value >= 'a' && value < 'z' || value >= 'A' && value < 'Z') 
     { 
      return (char)((int)value + 1); 
     } 
     if (value == 'z') return 'A'; 
     if (value == 'Z') 
     { 
      carry = true; 
      return 'a'; 
     } 
     throw new Exception(String.Format("Invalid character value: {0}", value)); 
    } 
} 
+0

+1. comme celui-ci le meilleur (encore mieux que le mien), a) ça marche (passe de toute façon mes tests), b) l'opération est plus claire et c) contient intrinsèquement un code défensif. Joli. ;) – AnthonyWJones

+0

Merci, tout d'abord je voulais qu'il soit lisible et facile à comprendre. – empi

0

Vous devez expliquer A) le fait que les lettres majuscules ont une valeur décimale inférieure dans la table Ascii que les minuscules. B) La table n'est pas continue A-Z-a-z - il y a des caractères entre Z et a.

public static string stepChar(string str) 
{ 
    return stepChar(str, str.Length - 1); 
} 

public static string stepChar(string str, int charPos) 
{ 
    return stepChar(Encoding.ASCII.GetBytes(str), charPos); 
} 

public static string stepChar(byte[] strBytes, int charPos) 
{ 
    //Escape case 
    if (charPos < 0) 
    { 
    //just prepend with a and return 
    return "a" + Encoding.ASCII.GetString(strBytes); 
    } 
    else 
    { 

    strBytes[charPos]++; 

    if (strBytes[charPos] == 91) 
    { 
     //Z -> a plus increment previous char 
     strBytes[charPos] = 97; 
     return stepChar(strBytes, charPos - 1);    } 
    else 
    { 
     if (strBytes[charPos] == 123) 
     { 
     //z -> A 
     strBytes[charPos] = 65; 
     } 

     return Encoding.ASCII.GetString(strBytes); 
    } 
    } 
} 

Vous aurez probablement besoin d'une vérification en place pour faire en sorte que la chaîne d'entrée ne contient que carbonise A-Za-z


Modifier Code tondu et a ajouté une nouvelle surcharge pour supprimer redondante octet [] -> string -> octet [] conversion

Proof http://geekcubed.org/random/strIncr.png

5

collection Tout à fait d'appro Ches, voici le mien: -

La fonction:

private static string IncrementString(string s) 
{ 
    byte[] vals = System.Text.Encoding.ASCII.GetBytes(s); 
    for (var i = vals.Length - 1; i >= 0; i--) 
    { 
    if (vals[i] < 90) 
    { 
     vals[i] += 1; 
     break; 
    } 
    if (vals[i] == 90) 
    { 
     if (i != 0) 
     { 
     vals[i] = 97; 
     continue; 
     } 
     else 
     { 
     return new String('a', vals.Length + 1); 
     } 
    } 

    if (vals[i] < 122) 
    { 
     vals[i] += 1; 
     break; 
    } 

    vals[i] = 65; 
    break; 
    } 

    return System.Text.Encoding.ASCII.GetString(vals); 
} 

Les tests

Console.WriteLine(IncrementString("a") == "b"); 
Console.WriteLine(IncrementString("z") == "A"); 
Console.WriteLine(IncrementString("Z") == "aa"); 
Console.WriteLine(IncrementString("aa") == "ab"); 
Console.WriteLine(IncrementString("az") == "aA"); 
Console.WriteLine(IncrementString("aZ") == "ba"); 
Console.WriteLine(IncrementString("zZ") == "Aa"); 
Console.WriteLine(IncrementString("Za") == "Zb"); 
Console.WriteLine(IncrementString("ZZ") == "aaa"); 
+0

+1 pour les boucles fantaisie - Je préfère ma fonction récursive plus;) – Ian

0

Je suis désolé, la question est en partie déclaré. J'ai édité la question pour qu'elle réponde aux exigences, sans l'édition la fonction finirait par n fois par pas en augmentant chaque mot de minuscule a en majuscule z sans "ré-analyser".

S'il vous plaît envisager de relire la question, y compris la partie éditée

0

C'est ce que je suis venu avec. Je ne compte pas sur la conversion ASCII int, et j'utilise plutôt un tableau de caractères. Cela devrait faire exactement ce que vous cherchez.

public static string Step(this string s) 
    { 
     char[] stepChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray(); 

     char[] str = s.ToCharArray(); 
     int idx = s.Length - 1; 

     char lastChar = str[idx]; 


     for (int i=0; i<stepChars.Length; i++) 
     { 
      if (stepChars[i] == lastChar) 
      { 
       if (i == stepChars.Length - 1) 
       { 
        str[idx] = stepChars[0]; 
        if (str.Length > 1) 
        { 
         string tmp = Step(new string(str.Take(str.Length - 1).ToArray())); 
         str = (tmp + str[idx]).ToCharArray(); 
        } 
        else 
         str = new char[] { stepChars[0], str[idx] }; 
       } 
       else 
        str[idx] = stepChars[i + 1]; 

       break; 
      } 
     } 

     return new string(str); 
    } 
0

Ceci est un cas particulier d'un système numérique. Il a la base de 52.Si vous écrivez un analyseur et une logique de sortie, vous pouvez faire n'importe quel type d'arithmétique, évidemment le +1 (++) ici. Les chiffres sont "a" - "z" et "A" à "Z" où "a" est zéro et "Z" est 51

Vous devez donc écrire un analyseur qui prend la chaîne et construit un int ou depuis longtemps. Cette fonction est appelée StringToInt() et est implémentée directement (transformer char en nombre (0..51) multiplier par 52 et prendre le caractère suivant)

Et vous avez besoin de la fonction inverse IntToString qui est également implémentée en ligne droite (modulo l'int avec 52 et transformer le résultat en chiffre, diviser l'int par 52 et répéter ceci jusqu'à ce que int soit nul)

Avec cette fonction, vous pouvez faire des choses comme ceci: IntToString (StringToInt ("ZZ") +1) // Sera "aaa"

+1

Pas si simple. Il n'y a pas de "chiffre zéro" depuis a! = Aa! = Aaa. Impossible d'utiliser Z non plus parce que aZ! = ZaZ – jnylen

0

Cela ressemble beaucoup à la façon dont les colonnes Excel fonctionneraient si elles étaient illimitées. Vous pouvez remplacer 52 par charsLength pour faciliter les modifications.

static class AlphaInt { 
    private static string chars = 
     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 

    public static string StepNext(string input) { 
     return IntToAlpha(AlphaToInt(input) + 1); 
    } 

    public static string IntToAlpha(int num) { 
     if(num-- <= 0) return "a"; 
     if(num % 52 == num) return chars.Substring(num, 1); 
     return IntToAlpha(num/52) + IntToAlpha(num % 52 + 1); 
    } 

    public static int AlphaToInt(string str) { 
     int num = 0; 
     for(int i = 0; i < str.Length; i++) { 
      num += (chars.IndexOf(str.Substring(i, 1)) + 1) 
        * (int)Math.Pow(52, str.Length - i - 1); 
     } 
     return num; 
    } 
} 
Questions connexes