How to increment letter combinations in c++ beyond 'z'?

拜拜、爱过 提交于 2019-12-02 02:14:20
qwr

General solution descriptions

Solution 1: Create base-26 system itself:

Assume that you have 26 letters. So first lets make 26 number system. We use 1 byte for each digit. We create an array of digits, and then we need to adjust sometimes when adding exceeds 26.

Let's assume your current digit is 25. We add 7 to it, and then we need to handle the overflow, assuming 256 (1 byte) as maximum, and our digit limit is 26. Hence the adjustment will be 256-26=230. (We do this calculation on short (16 bits), so we get overflow at 26+7=33 -> 33+230=263. Therefore, the higher byte will be 1, the lower byte will be 7.)

Having calculated the overflow threshold (above it was 1), we can then add it to next coming digit and do the same if overflow occur.

Finally, for display we just add 65 ('A') to each of the two bytes. Our last byte will be '\0' null terminating so we could turn it into a string.

Solution 2 Perform all calculations and then convert it to a 26-base number:

In this case,

number/26 = x

and

remainder r1 = (number%26)

We store r1 to a byte.

x/26 = x1

and

remainder r2 = (x%26)

We store r2 to the next byte.

x1/26 = x2

and

remainder r3 = (x%26)

We store r3 to the next byte. We get a string r3 r2 r1 '\0' and then add 65 'A' to each byte.

You could do it in two nested loops, both from 'A' to 'Z', then just append both characters together. Put the results in a vector.

To get the single-letter 'A' to 'Z', add them to the vector before the nested loops.

Do this:

x = (x + 5 > 'Z' ? x + 5 - 'Z' + 'A' : x + 5);
y = (y + 5 > 'Z' ? y + 5 - 'Z' + 'A' : y + 5);

The result you're trying to produce can be viewed as a base-27 number, using the characters 'A' through 'Z' as the 26 digits. It is a little unusual, in that 'A' is really '0', but when you get to a two-digit number, it's "AA", (where a normal two-digit would be BA -- i.e., just like the next number after 9 is 10). We have to do a fairly minor adjustment to compensate for that oddity.

You can do the counting with a normal int (or whatever) and then convert to base 27 when needed. Since I don't think it supports more columns than that, I've restricted this version to 2 digits, though extending to more would be fairly trivial:

std::string to_excel_column(int input) { 
    int digit1 = input % 26;
    int digit2 = input / 26;

    std::string result;
    if (input > 26)
        result.push_back(digit2 + 'A' - 1);
    result.push_back(digit1 + 'A');
    return result;
}

You could, as one option, create a new data type consisting of two characters (something like std::pair<char,char> and define arithmetics for this.

However, I think it would be simpler to keep using int (or unsigned int) for the counting and arithmetic, and define a function to_col_heading which translates the integer into a one/two-character column heading for Excel:

#include <string>
#include <iostream>
#include <stdexcept>
#include <iterator>

/* This is the function that performs
   the transformation. */    
std::string to_col_heading(unsigned i)
{
  if (i > 26+26*26)
    throw std::overflow_error
      ("Value too large for transformation into Excel column heading.");

  char char1 = 'A' + (i / 26) - 1;
  char char2 = 'A' + (i % 26);

  if (char1 < 'A')
    return std::string(&char2,&char2 + 1);
  else
    return std::string(&char1,&char1 + 1) + char2;
}

int main()
{
  std::ostream_iterator<std::string> out(std::cout,", ");
  for (unsigned i = 0 ; i < 26+26*26 ; i += 5)
    *out = to_col_heading(i);

  std::cout << std::endl;
  return 0;
}

Output:

A, F, K, P, U, Z, AE, AJ, AO, AT, AY, BD, BI, BN, BS, BX, CC, CH, CM, CR, CW, DB, DG, DL, DQ, DV, EA, EF, EK, EP, EU, EZ, FE, FJ, FO, FT, FY, GD, GI, GN, GS, GX, HC, HH, HM, HR, HW, IB, IG, IL, IQ, IV, JA, JF, JK, JP, JU, JZ, KE, KJ, KO, KT, KY, LD, LI, LN, LS, LX, MC, MH, MM, MR, MW, NB, NG, NL, NQ, NV, OA, OF, OK, OP, OU, OZ, PE, PJ, PO, PT, PY, QD, QI, QN, QS, QX, RC, RH, RM, RR, RW, SB, SG, SL, SQ, SV, TA, TF, TK, TP, TU, TZ, UE, UJ, UO, UT, UY, VD, VI, VN, VS, VX, WC, WH, WM, WR, WW, XB, XG, XL, XQ, XV, YA, YF, YK, YP, YU, YZ, ZE, ZJ, ZO, ZT, ZY,

To support up to 3 (alphabetical) digits for the column headings, the function below works. It handles the first 26 values completely separately to simplify the calculation of the rest. I've also introduced some static constants to deal in a more consistent way with the powers of 26. For three digits, you need 260, 261 and 262, but the system can be expanded to cover any number of digits.

I am sure there a ways to optimize this for speed; this was not the focus here.

std::string to_col_heading(unsigned i)
{
  const static unsigned pow1 = 26;
  const static unsigned pow2 = 26*26;
  const static unsigned max  = pow1 + pow2 + 26 * pow2;

  if (i < pow1)
    {
      char c = 'A' + i % pow1;
      return std::string(&c,&c+1);
    }

  if (i > max)
    throw std::overflow_error
      ("Value too large for transformation into Excel column heading.");

  i -= pow1;
  char char1 = 'A' + (i / pow2) - 1;
  char char2 = 'A' + (i%pow2) / pow1;
  char char3 = 'A' + i % pow1;

  if (char1 < 'A')
    return std::string(&char2,&char2 + 1) + char3;
  else
    return std::string(&char1,&char1 + 1) + char2 + char3;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!