Как сделать следующий шаг строки. С#

вопрос сложный, но я объясню это в деталях.

цель состоит в том, чтобы сделать функцию, которая будет возвращать следующий "шаг" данной строки.

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"

пока здесь это довольно легко, но, учитывая, что вход является строкой, он может содержать более 1 символов, и функция должна вести себя так.

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

и так далее...

Это точно не нужно расширять базовую строку класс.

Я попытался разобраться с каждым значением ASCII символов, но застрял со строками, содержащими 2 символа.

Я был бы очень признателен, если кто-то может предоставить полный код функции.

спасибо заранее.

редактировать *Мне жаль, что я забыл упомянуть ранее, что функция "повторной обработки" самостоятельно созданную строку, когда ее длина достигает 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";
........

прости, что не упомянул об этом. ранее, прочитав некоторые ответы, я понял, что проблема была под вопросом.

без этого функция всегда будет производить символ "a"n раз после окончания этапа.

10 ответов


Примечание: ответ неправильно, поскольку " aa "должно следовать за"Z"... (см. комментарии ниже)

вот алгоритм, который может сработать:

каждая "строка" представляет собой число для заданной базы (здесь: дважды количество букв в алфавите).

следующий шаг, таким образом, может быть вычислен путем разбора строки"number" обратно в int, добавления 1, а затем форматирования ее обратно в основа.

пример:

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

теперь ваша проблема сводится к разбору строки как числа на заданную базу и ее переформатированию. Быстро погуглив можно предположить, что это страницы: http://everything2.com/title/convert+любой+номер+для+десятичное

Как это реализовать?

  • таблица подстановки букв к их соответствующему номеру: a=1, b=2, c=3,... Y = ?, Z = 0
  • чтобы разобрать строку на число, прочитайте символы в обратном порядке, просматривая цифры и складывая их:
    • "ab" - > 2 * BASE^0 + 1 * BASE^1
    • С базой, являющейся числом "цифр" (2 количество букв в алфавите, это 48?)

EDIT: эта ссылка выглядит еще более многообещающей:http://www.citidel.org/bitstream/10117/20/12/convexp.html


довольно коллекция подходов, вот моя: -

Функции:

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

Тесты

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

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

разделите входную строку на столбцы и обработайте каждую справа налево, как если бы это была базовая арифметика. Примените любой код, который работает с одним столбцом, к каждому столбцу. Когда вы получаете Z, вы "увеличиваете" следующий левый столбец, используя тот же алгоритм. Если следующей левой колонки нет, поставьте "а".


Мне жаль, что вопрос задан частично. Я отредактировал вопрос так, чтобы он отвечал требованиям, без редактирования функция в конечном итоге будет иметь n раз за шагом увеличивая каждое слово из нижнего регистра a в верхний регистр z без "повторного разбора".

пожалуйста, подумайте о перечитывании вопроса, включая отредактированную часть


вот что я придумал. Я не полагаюсь на преобразование ASCII int и скорее использую массив символов. Это должны сделать именно то, что вы ищете.

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

Это частный случай системы счисления. Он имеет основание 52. Если вы напишете некоторый парсер и логику вывода, вы можете сделать любую арифметику, очевидно, +1 (++) здесь. Цифры "a" - "z" и "A" до "Z", где "a" - ноль, а " Z " - 51

поэтому вам нужно написать парсер, который берет строку и строит из нее int или long. Эта функция называется StringToInt () и реализуется прямо вперед (преобразует символ в число (0..51) умножьте на 52 и возьмите следующий символ)

и вам нужна обратная функция IntToString, которая также реализует прямо вперед (по модулю int с 52 и преобразует результат в цифру, делит int на 52 и повторяет это, пока int не будет равен null)

с помощью этих функций вы можете делать такие вещи: IntToString (StringToInt ("ZZ") +1 ) // будет"aaa"


вам нужно учитывать A) тот факт, что заглавные буквы имеют меньшее десятичное значение в таблице Ascii, чем строчные. B) таблица не является непрерывной A-Z-a - z-между Z и 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);
    }
  }
}

вероятно, вам понадобится некоторая проверка, чтобы убедиться, что входная строка содержит только символы A-Za-z


редактировать убранный код и добавлена новая перегрузка для удаления избыточного байта [] - > строка - > байт[] преобразование

доказательство http://geekcubed.org/random/strIncr.png


это очень похоже на то, как столбцы Excel будут работать, если они будут неограниченными. Вы можете изменить 52 на ссылочные символы.Длина для более легкого изменения.

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

LetterToNum должна быть функция, которая отображает " a "на 0 и" Z " на 51. NumToLetter обратный.

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