2009-09-04 5 views
6

La pregunta es complicada, pero la explicaré en detalle.Cómo hacer el siguiente paso de una cadena. C#

El objetivo es hacer una función que devolverá el siguiente "paso" de la cadena dada.

Por ejemplo

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" 

Hasta aquí es bastante fácil, pero teniendo en cuenta que la entrada es una cadena que puede contener más de 1 caracteres y la función debe comportarse de esta manera.

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"; 

y así sucesivamente ...

Esto no significa exactamente necesita para extender la clase String base.

Traté de resolverlo con cada uno de los caracteres de los valores ASCII pero me atasqué con las cadenas que contenían 2 caracteres.

Realmente apreciaría si alguien puede proporcionar el código completo de la función.

Gracias de antemano.

EDITAR * Siento haber olvidado mencionar antes de que la función de "reanálisis" la cadena de auto generada cuando su longitud alcanza n.

continuation of this function will be smth like this. for example n = 3 
String.Step("aaa"); // = "aab"; 
String.Step("aaZ"); // = "aba"; 
String.Step("aba"); // = "abb"; 
String.Step("abb"); // = "abc"; 
String.Step("abZ"); // = "aca"; 
..... 
String.Step("zzZ"); // = "zAa"; 
String.Step("zAa"); // = "zAb"; 
........ 

lo siento que no he mencionado antes, después de leer algunas respuestas me di cuenta de que el problema estaba en cuestión.

Sin esto, la función siempre producirá el carácter "a" n veces después del final del paso.

+4

¿Por qué no publicar lo que ha intentado? –

+0

La entrada siempre es a-zA-Z – George

+0

Lo siento, olvidé agregar la pregunta principal: S. Pregunta editada. – George

Respuesta

11

NOTA: Esta respuesta es incorrecta , como "AA" deberá seguir después de "Z" ... (ver comentarios abajo)

Aquí es un algoritmo que podría funcionar:

cada "cadena" representa un número para una base determinada (aquí: dos veces el recuento de letras en el alfabeto).

El siguiente paso puede así calcularse mediante el análisis de la cadena "number" de nuevo en un int, agregando 1 y luego formateándolo de nuevo en la base.

Ejemplo:

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

Ahora el problema se reduce a analizar la cadena como un número en una base dada y reformateándola. Un rápido googlear sugiere esta página: http://everything2.com/title/convert+any+number+to+decimal

¿Cómo implementar esto?

  • una tabla de búsqueda para las letras a su número correspondiente: a = 1, b = 2, c = 3, ... Y =?, Z = 0
  • a analizar una cadena a número, lea los caracteres en orden inverso, mirando los números y sumarlos:
    • "ab" -> 2 * BASE^0 + 1 * BASE^1
    • con la BASE es el número de "dígitos" (2 recuento de letras en el alfabeto, es decir que el 48?)

EDIT: Este enlace se ve aún más prometedor: http://www.citidel.org/bitstream/10117/20/12/convexp.html

+0

¿Podría explicarlo más brevemente como publicar un pseudo código o, si es posible, completo? Gracias – George

+1

No es tan simple, ¿qué letra representa 0, a? ¿Cuál es el valor int de "a" y cuál es el valor int de "aa"? – AnthonyWJones

+0

@Anthony: A partir de los ejemplos en la pregunta, creo que se trata simplemente de Base52 invertido. 'a = 0, A = 26, aa = 52' – dtb

0

Divida la cadena de entrada en columnas y procese cada una, de derecha a izquierda, como lo haría si fuera una aritmética básica. Aplique el código que tenga que funcione con una sola columna a cada columna. Cuando obtienes una Z, 'incrementas' la columna siguiente a la izquierda usando el mismo algoritmo. Si no hay una columna a la izquierda, ingrese una 'a'.

-1

LetterToNum debe ser una función que asigna "a" a 0 y "Z" a 51. NumToLetter el inverso.

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

De nuevo un enfoque basado en Base52, esto no funciona. La primera línea devolverá 1 para "a" o "aa" o "aaa". – AnthonyWJones

+0

De hecho, se perdió el 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. como este, el mejor (incluso mejor que el mío), a) porque funciona (pasa mi conjunto de pruebas de todos modos), b) la operación es más clara yc) contiene intrínsecamente algún código defensivo. Buena esa. ;) – AnthonyWJones

+0

Gracias, antes que nada, quería que fuera legible y fácil de entender. – empi

0

necesita tener en cuenta para A) el hecho de que las letras mayúsculas tienen un valor decimal inferior en la tabla ASCII que las minúsculas. B) La tabla no es continua A-Z-a-z - hay caracteres entre Z y 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); 
    } 
    } 
} 

usted probablemente querrá algunas comprobaciones para garantizar que la cadena de entrada contiene sólo carboniza A-Za-z


Editar puso en orden de código y crear nueva sobrecarga para eliminar redundante byte [] -> string -> byte [] conversión

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

5

colección bastante de approa ches, aquí está la mía: -

La Función:

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); 
} 

Las Pruebas

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 para bucles de fantasía - Prefiero mi función recursiva más;) – Ian

0

Lo siento la cuestión se afirma en parte. He editado la pregunta para que cumpla con los requisitos, sin la edición, la función terminaría con n veces, paso a paso, aumentando cada palabra de minúscula a mayúscula z sin "volver a analizarla".

Por favor, considere la posibilidad de volver a leer la pregunta, incluyendo la parte editada

0

Esto es lo que ocurrió. No estoy confiando en la conversión int ASCII, sino que estoy usando una matriz de caracteres. Esto debería hacer exactamente lo que estás buscando.

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

Este es un caso especial de un sistema numérico. Tiene la base de 52.Si escribe un analizador y una lógica de salida, puede hacer cualquier tipo de aritmética, obviamente, el +1 (++) aquí. Los dígitos son "a" - "z" y "A" a "Z" donde "a" es cero y "Z" es 51

Así que debe escribir un analizador que toma la cadena y crea una int o mucho más de eso. Esta función se llama StringToInt() y se implementa de forma directa (transformar char en el número (0..51) multiplicar con 52 y tomar el siguiente carácter)

Y necesita la función inversa IntToString que también se implementa directamente (modulo int con 52 y transformar resultado a dígito, divide el int por 52 y repite esto hasta que int es nulo)

Con estas funciones puedes hacer cosas como esta: IntToString (StringToInt ("ZZ") +1) // Será "aaa"

+1

No es tan simple. No hay un "dígito cero" dado que a! = Aa! = Aaa. No se puede usar Z tampoco porque aZ! = ZaZ – jnylen

0

Esto es muy parecido a cómo funcionarían las columnas de Excel si no tuvieran límites. Puede cambiar 52 a caracteres de referencia. Longitud para facilitar la modificación.

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; 
    } 
} 
Cuestiones relacionadas