18

I need to increment a String in java from "aaaaaaaa" to "aaaaaab" to "aaaaaac" up through the alphabet, then eventually to "aaaaaaba" to "aaaaaabb" etc. etc.

Is there a trick for this?

dacracot
  • 22,002
  • 26
  • 104
  • 152
  • 11
    Remember, brute-force password cracking is unethical. – bzlm Dec 04 '08 at 21:38
  • 5
    doesn't matter, because when he has reached zzzzzzzz he'll be neither ethical nor unethical, but dead. –  Dec 05 '08 at 01:07
  • 1
    He didn't say which alphabet! So technically zzzzzzz may not be the limit. By öööööööö, he'll be even deader. – bzlm Dec 05 '08 at 14:29

13 Answers13

55

You're basically implementing a Base 26 number system with leading "zeroes" ("a").

You do it the same way you convert a int to a base-2 or base-10 String, but instead of using 2 or 10, you use 26 and instead of '0' as your base, you use 'a'.

In Java you can easily use this:

public static String base26(int num) {
  if (num < 0) {
    throw new IllegalArgumentException("Only positive numbers are supported");
  }
  StringBuilder s = new StringBuilder("aaaaaaa");
  for (int pos = 6; pos >= 0 && num > 0 ; pos--) {
    char digit = (char) ('a' + num % 26);
    s.setCharAt(pos, digit);
    num = num / 26;
  }
  return s.toString();
}

The basic idea then is to not store the String, but just some counter (int an int or a long, depending on your requirements) and to convert it to the String as needed. This way you can easily increase/decrease/modify your counter without having to parse and re-create the String.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
14

The following code uses a recursive approach to get the next string (let's say, from "aaaa" to "aaab" and so on) without the need of producing all the previous combinations, so it's rather fast and it's not limited to a given maximum string length.

public class StringInc {
 public static void main(String[] args) {
   System.out.println(next("aaa")); // Prints aab

   System.out.println(next("abcdzz")); // Prints abceaa

   System.out.println(next("zzz")); // Prints aaaa
 }

 public static String next(String s) {
   int length = s.length();
   char c = s.charAt(length - 1);

   if(c == 'z')
     return length > 1 ? next(s.substring(0, length - 1)) + 'a' : "aa";

   return s.substring(0, length - 1) + ++c;
 }
}

As some folks pointed out, this is tail recursive, so you can reformulate it replacing the recursion with a loop.

cyberz
  • 884
  • 1
  • 8
  • 16
4

Increment the last character, and if it reaches Z, reset it to A and move to the previous characters. Repeat until you find a character that's not Z. Because Strings are immutable, I suggest using an array of characters instead to avoid allocating lots and lots of new objects.

public static void incrementString(char[] str)
{
    for(int pos = str.length - 1; pos >= 0; pos--)
    {
        if(Character.toUpperCase(str[pos]) != 'Z')
        {
            str[pos]++;
            break;
        }
        else
            str[pos] = 'a';
    }
}
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
1

you can use big integer's toString(radix) method like:

import java.math.BigInteger;
public class Strings {
    Strings(final int digits,final int radix) {
        this(digits,radix,BigInteger.ZERO);
    }
    Strings(final int digits,final int radix,final BigInteger number) {
        this.digits=digits;
        this.radix=radix;
        this.number=number;
    }
    void addOne() {
        number=number.add(BigInteger.ONE);
    }
    public String toString() {
        String s=number.toString(radix);
        while(s.length()<digits)
            s='0'+s;
        return s;
    }
    public char convert(final char c) {
        if('0'<=c&&c<='9')
            return (char)('a'+(c-'0'));
        else if('a'<=c&&c<='p')
            return (char)(c+10);
        else throw new RuntimeException("more logic required for radix: "+radix);
    }
    public char convertInverse(final char c) {
        if('a'<=c&&c<='j')
            return (char)('0'+(c-'a'));
        else if('k'<=c&&c<='z')
            return (char)(c-10);
        else throw new RuntimeException("more logic required for radix: "+radix);
    }
    void testFix() {
        for(int i=0;i<radix;i++)
            if(convert(convertInverse((char)('a'+i)))!='a'+i)
                throw new RuntimeException("testFix fails for "+i);
    }
    public String toMyString() {
        String s=toString(),t="";
        for(int i=0;i<s.length();i++)
            t+=convert(s.charAt(i));
        return t;
    }
    public static void main(String[] arguments) {
        Strings strings=new Strings(8,26);
        strings.testFix();
        System.out.println(strings.number.toString()+' '+strings+' '+strings.toMyString());
        for(int i=0;i<Math.pow(strings.radix,3);i++)
            try {
                strings.addOne();
                if(Math.abs(i-i/strings.radix*strings.radix)<2)
                    System.out.println(strings.number.toString()+' '+strings+' '+strings.toMyString());
            } catch(Exception e) {
                System.out.println(""+i+' '+strings+" failed!");
            }
    }
    final int digits,radix;
    BigInteger number;
}
Ray Tayek
  • 9,841
  • 8
  • 50
  • 90
  • too complicated, probably waaaay over the OPs head. put at least some comments in there! –  Dec 05 '08 at 12:31
  • Over my head, pleeeeeeeeeeeeeeeeeease. Trying it since the (so far) accepted answer hit maxint. – dacracot Dec 05 '08 at 15:16
  • i am using the same base 26 trick to get the number in base 26. i need to convert the characters to what the poster wants (hence convert and convertInverse). you will need to modify the for loop so that i is a big decimal if you want to go all the way to zzzzzzzz. – Ray Tayek Dec 09 '08 at 02:18
1

I'd have to agree with @saua's approach if you only wanted the final result, but here is a slight variation on it in the case you want every result.

Note that since there are 26^8 (or 208827064576) different possible strings, I doubt you want them all. That said, my code prints them instead of storing only one in a String Builder. (Not that it really matters, though.)

  public static void base26(int maxLength) {
    buildWord(maxLength, "");
  }
  public static void buildWord(int remaining, String word)
  {
    if (remaining == 0)
    {
      System.out.println(word);
    }
    else
    {
      for (char letter = 'A'; letter <= 'Z'; ++letter)
      {
        buildWord(remaining-1, word + letter);
      }
    }
  }

  public static void main(String[] args)
  {
    base26(8);
  }
Rob Rolnick
  • 8,519
  • 2
  • 28
  • 17
0

I would create a character array and increment the characters individually. Strings are immutable in Java, so each change would create a new spot on the heap resulting in memory growing and growing.

With a character array, you shouldn't have that problem...

Nicholas Mancuso
  • 11,599
  • 6
  • 45
  • 47
0

Have an array of byte that contain ascii values, and have loop that increments the far right digit while doing carry overs.

Then create the string using

public String(byte[] bytes, String charsetName)

Make sure you pass in the charset as US-ASCII or UTF-8 to be unambiguous.

Pyrolistical
  • 27,624
  • 21
  • 81
  • 106
0

Just expanding on the examples, as to Implementation, consider putting this into a Class... Each time you call toString of the Class it would return the next value:

public class Permutator {

    private int permutation;

    private int permutations; 

    private StringBuilder stringbuilder;

    public Permutator(final int LETTERS) {

        if (LETTERS < 1) {
            throw new IllegalArgumentException("Usage: Permutator( \"1 or Greater Required\" \)");
        }

        this.permutation = 0;

        // MAGIC NUMBER : 26 = Number of Letters in the English Alphabet 
        this.permutations = (int) Math.pow(26, LETTERS);

        this.stringbuilder = new StringBuilder();

        for (int i = 0; i < LETTERS; ++i) {
            this.stringbuilder.append('a');
        }
    }

    public String getCount() {

        return String.format("Permutation: %s of %s Permutations.", this.permutation, this.permutations);
    }

    public int getPermutation() {

        return this.permutation;
    }

    public int getPermutations() {

        return this.permutations;
    }

    private void permutate() {

        // TODO: Implement Utilising one of the Examples Posted.
    } 

    public String toString() {

        this.permutate();

        return this.stringbuilder.toString();
    }
}    
Ande Turner
  • 7,096
  • 19
  • 80
  • 107
0

Building on the solution by @cyberz, the following code is an example of how you could write a recursive call which can be optimized by a compiler that supports Tail Recursion.

The code is written in Groovy, since it runs on the JVM, its syntax closely resembles Java and it's compiler supports tail recursion optimization

static String next(String input) {
    return doNext(input, "")
}

@TailRecursive
@CompileStatic
static String doNext(String input, String result) {
    if(!self) {
        return result
    }

    final String last = input[-1]
    final String nonLast = self.substring(0, input.size()-1)
    if('z' == last) {
        return doNext(nonLast, (nonLast ? 'a' : 'aa') + result)
    }

    return doNext('', nonLast + (((last as Character) + 1) as Character).toString() + result)
}
Community
  • 1
  • 1
geoand
  • 60,071
  • 24
  • 172
  • 190
0

Since none of the answers were useful to me, I wrote my own code:

/**
 * Increases the given String value by one. Examples (with min 'a' and max 'z'): <p>
 * 
 * - "aaa" -> "aab" <br>
 * - "aab" -> "aac" <br>
 * - "aaz" -> "aba" <br>
 * - "zzz" -> "aaaa" <br>
 * 
 * @param s
 * @param min lowest char (a zero)
 * @param max highest char (e.g. a 9, in a decimal system)
 * @return increased String by 1
 */
public static String incString(String s, char min, char max) {
    char last = s.charAt(s.length() - 1);
    if (++last > max)
        return s.length() > 1 ? incString(s.substring(0, s.length()-1), min, max) + min : "" + min + min;
    else
        return s.substring(0, s.length()-1) + last;
}
Betalord
  • 109
  • 7
0
public static String incrementString(String string)
{
    if(string.length()==1)
    {
        if(string.equals("z"))
            return "aa";
        else if(string.equals("Z"))
            return "Aa";
        else
            return (char)(string.charAt(0)+1)+"";
    }   
    if(string.charAt(string.length()-1)!='z')
    {
        return string.substring(0, string.length()-1)+(char)(string.charAt(string.length()-1)+1);
    }
    return incrementString(string.substring(0, string.length()-1))+"a";
}

Works for all standard string containing alphabets

0

I have approach using for loop which is fairly simple to understand. based on [answer]: https://stackoverflow.com/a/2338415/9675605 cyberz answer. This also uses org.apache.commons.lang3.ArrayUtils. to insert letter on first position. you can create your own util for it. If someone finds helpful.

import org.apache.commons.lang3.ArrayUtils;
public class StringInc {
 public static void main(String[] args) {
   System.out.println(next("aaa")); // Prints aab

   System.out.println(next("abcdzz")); // Prints abceaa

   System.out.println(next("zzz")); // Prints aaaa
 }

 public static String next(String str) {
    boolean increment = true;
    char[] arr = str.toCharArray();
    for (int i = arr.length - 1; i >= 0 && increment; i--) {
      char letter = arr[i];
      if (letter != 'z') {
        letter++;
        increment = false;
      } else {
        letter = 'a';
      }
      arr[i] = letter;
    }
    if (increment) {
      arr = ArrayUtils.insert(0, arr, 'a');
    }
    return new String(arr);
}
Vibhanshu
  • 1
  • 1
-3

It's not much of a "trick", but this works for 4-char strings. Obviously it gets uglier for longer strings, but the idea is the same.

char array[] = new char[4];
for (char c0 = 'a'; c0 <= 'z'; c0++) {
  array[0] = c0;
  for (char c1 = 'a'; c1 <= 'z'; c1++) {
    array[1] = c1;
    for (char c2 = 'a'; c2 <= 'z'; c2++) {
      array[2] = c2;
      for (char c3 = 'a'; c3 <= 'z'; c3++) {
        array[3] = c3;
        String s = new String(array);
        System.out.println(s);
      }
    }
  }
}
Clayton
  • 920
  • 1
  • 6
  • 13
  • This is is faster and easier to understand. Less is more. – dacracot Dec 05 '08 at 17:28
  • I tested it in Java, but the basic idea should work fine in C. That deep nested loop is definitely ugly, but it gets the job done: enumerate all possible combinations of 'a'..'z'. As others have pointed out, creating the String object is expensive, so it might be better to use the array directly. – Clayton Dec 06 '08 at 18:26
  • I really liked @saua's solution though. It gives nice encapsulation of the string generation function. Using that approach lets you write a much simpler loop for doing your real work, something like this: for (int i=0; i<=MAX; i++) { String s = base26(i); doSomethingInteresting(s); } – Clayton Dec 06 '08 at 18:32