2009-09-04 8 views
6

La domanda è complicata ma la spiegherò nei dettagli.Come eseguire il prossimo passo di una stringa. C#

L'obiettivo è eseguire una funzione che restituirà il "passo" successivo della stringa specificata.

Per esempio

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" 

Fin qui è abbastanza facile, ma tenendo a mente che l'input è una stringa che può contenere più di 1 caratteri e la funzione deve comportarsi in questo modo.

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

e così via ...

Questo non è esattamente bisogno di estendere la classe base String.

Ho provato a elaborare i valori ASCII di ciascun carattere ma sono rimasto bloccato con stringhe contenenti 2 caratteri.

Sarei davvero grato se qualcuno possa fornire il codice completo della funzione.

Grazie in anticipo.

EDIT * Mi dispiace di aver dimenticato di dire in precedenza che la funzione "di analisi", la stringa di sé generato quando la sua lunghezza raggiunge 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"; 
........ 

Mi dispiace che non ho menzionato in precedenza, dopo aver letto alcune risposte mi sono reso conto che il problema era in questione.

In caso contrario, la funzione produrrà sempre il carattere "a" n volte dopo la fine del passaggio.

+4

Perché non pubblicare ciò che hai provato? –

+0

L'input è sempre a-zA-Z – George

+0

Mi dispiace ho dimenticato di aggiungere la domanda principale: S. Domanda modificata. – George

risposta

11

NOTA: Questa risposta è errata , come "aa" dovrebbe seguire dopo "Z" ... (vedi commenti qui sotto)

Ecco un algoritmo che potrebbe funzionare:

ogni "stringa" rappresenta un numero per una data base (qui: il doppio delle lettere dell'alfabeto).

Il passaggio successivo può quindi essere calcolato analizzando il "numero", riportandolo in un int, aggiungendo 1 e quindi formattandolo nuovamente alla base.

Esempio:

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

Ora il problema si riduce a parsing della stringa come un numero ad una data base e riformattazione. Un quick googling suggerisce questa pagina: http://everything2.com/title/convert+any+number+to+decimal

Come implementare questo?

  • una tabella di ricerca per le lettere al loro numero corrispondente: a = 1, b = 2, c = 3, ... Y =?, Z = 0
  • per analizzare una stringa in numero, leggere i caratteri in ordine inverso, guardando i numeri e li sommando:
    • "ab" -> 2 * BASE^0 + 1 * BASE^1
    • con BASE è il numero di "cifre" (2 conteggio di lettere dell'alfabeto, è che il 48?)

EDIT: Questo link sembra ancora più promettente: http://www.citidel.org/bitstream/10117/20/12/convexp.html

+0

Potresti spiegarlo più brevemente come postare uno pseudo codice o, se possibile, uno completo. Grazie – George

+1

In realtà non è così semplice, quale lettera rappresenta 0, a? Qual è il valore int di "a" e qual è il valore int di "aa"? – AnthonyWJones

+0

@Anthony: dagli esempi nella domanda, penso che sia semplicemente invertito Base52. 'a = 0, A = 26, aa = 52' – dtb

0

Dividi la stringa di input in colonne ed elaborale ciascuna, da destra a sinistra, come se fosse un'aritmetica di base. Applica il codice che hai che funziona con una singola colonna per ogni colonna. Quando si ottiene una Z, si "incrementa" la colonna successiva sinistra usando lo stesso algoritmo. Se non c'è una colonna successiva a sinistra, inserisci un 'a'.

-1

LetterToNum deve essere una funzione che associa "a" a 0 e "Z" a 51. NumToLetter l'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

Ancora un approccio basato su Base52, questo non funziona: la prima riga restituirà 1 per "a" o "aa" o "aaa". – AnthonyWJones

+0

Infatti, mancava il 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. come questo è il migliore (anche meglio del mio), a) perché funziona (passa comunque il mio set di test), b) l'operazione è più chiara e c) contiene intrinsecamente un codice difensivo. Ben fatto. ;) – AnthonyWJones

+0

Grazie, prima di tutto volevo che fosse leggibile e facile da capire. – empi

0

È necessario tenere conto di A) il fatto che le lettere maiuscole hanno un valore decimale inferiore nella tabella ASCII di quelli minuscole. B) La tabella non è continua A-Z-a-z - ci sono caratteri tra Z e 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); 
    } 
    } 
} 

Probabilmente vorrà qualche controllo in atto per garantire che la stringa di input contiene solo carbonizza A-Za-z


Modifica riordinato il codice e ha aggiunto nuova sovraccarico per rimuovere ridondanti byte [] -> string -> byte [] conversione

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

5

Piuttosto collezione di approa ches, qui è la mia: -

La funzione:

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

I test

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 per i loop di fantasia - preferisco la mia funzione ricorsiva di più;) – Ian

0

mi dispiace la domanda è dichiarato in parte. Ho modificato la domanda in modo che soddisfi i requisiti, senza la modifica la funzione finirebbe con un n volte passo dopo passo aumentando ogni parola da minuscola a a maiuscola z senza "ri-analizzarla".

Si prega di considerare rileggendo la questione, compresa la parte editata

0

Questo è quello che mi è venuta. Non sto facendo affidamento sulla conversione ASCII int e sto piuttosto usando una serie di caratteri. Questo dovrebbe fare esattamente quello che stai cercando.

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

Questo è un caso speciale di un sistema numerale. Ha la base di 52.Se scrivi un parser e una logica di output puoi fare qualsiasi tipo di aritmetica e ovviamente il +1 (++) qui. Le cifre sono "a" - "z" e "A" a "Z" dove "a" è zero e "Z" è 51

Quindi devi scrivere un parser che prende la stringa e costruisce un int o molto tempo dopo. Questa funzione si chiama StringToInt() e viene implementata direttamente (trasforma il carattere in numero (0..51) moltiplicato con 52 e prende il carattere successivo)

E hai bisogno della funzione di inversione IntToString che è anche implementet straight forward (modulo l'int con 52 e trasforma risultato in cifra, dividi l'int per 52 e ripeti fino a quando int è nullo)

Con queste funzioni puoi fare cose del genere: IntToString (StringToInt ("ZZ") +1) // Sarà "aaa"

+1

Non è così semplice. Non esiste una "cifra zero" poiché a! = Aa! = Aaa. Non puoi usare Z neanche perché aZ! = ZaZ – jnylen

0

Questo è molto simile al modo in cui le colonne di Excel funzionerebbero se fossero illimitate. È possibile modificare 52 per fare riferimento ai caratteri. Lunghezza per una modifica più semplice.

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; 
    } 
} 
Problemi correlati