Мне нужно увеличить строку в java от "aaaaaaaa" до "aaaaaab" до "aaaaaac" вверх по алфавиту, а затем, в конечном итоге, до "aaaaaaba" до "aaaaaabb" и т.д. и т.д.
Есть ли трюк для этого?
Мне нужно увеличить строку в java от "aaaaaaaa" до "aaaaaab" до "aaaaaac" вверх по алфавиту, а затем, в конечном итоге, до "aaaaaaba" до "aaaaaabb" и т.д. и т.д.
Есть ли трюк для этого?
Это не очень "трюк", но это работает для строк < char. Очевидно, он становится более уродливым для более длинных строк, но идея одна и та же.
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);
}
}
}
}
В основном вы реализуете базовую систему с номером 26 с ведущими "нулями" ( "a" ).
Вы делаете это так же, как конвертируете int в base-2 или base-10 String, но вместо использования 2 или 10 вы используете 26, а вместо "0" в качестве базы вы используете "a",.
В Java вы можете легко использовать это:
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();
}
Основная идея состоит в том, чтобы не хранить String, а просто некоторый счетчик (int int или long, в зависимости от ваших требований) и преобразовать его в String по мере необходимости. Таким образом вы можете легко увеличивать/уменьшать/изменять свой счетчик без необходимости синтаксического анализа и повторного создания строки.
Следующий код использует рекурсивный метод для получения следующей строки (скажем, от "aaaa" до "aaab" и т.д.) без необходимости производить все предыдущие комбинации, поэтому он довольно быстрый и не ограничен заданная максимальная длина строки.
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;
}
}
Приращение последнего символа, и если он достигнет Z, reset он - A и переместится к предыдущим символам. Повторяйте, пока не найдете символ, а не Z. Поскольку строки неизменяемы, я предлагаю вместо этого использовать массив символов, чтобы не выделять много и много новых объектов.
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';
}
}
вы можете использовать метод большого целого числа toString (radix), например:
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;
}
Я бы согласился с подходом @saua, если вы только хотели получить конечный результат, но здесь есть небольшая вариация в случае, если вы хотите получить каждый результат.
Заметим, что, так как есть 26 ^ 8 (или 208827064576) разных возможных строк, я сомневаюсь, что вы хотите их всех. Тем не менее, мой код печатает их вместо хранения только одного в String Builder. (Не то чтобы это действительно важно.)
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);
}
Я бы создал массив символов и увеличил символы отдельно. Строки неизменны в Java, поэтому каждое изменение создаст новое пятно на куче, что приведет к росту и росту памяти.
С массивом символов у вас не должно быть этой проблемы...
Имейте массив байтов, которые содержат значения ascii, и имеют цикл, который увеличивает дальнюю правую цифру при выполнении переноса.
Затем создайте строку, используя
public String(byte[] bytes, String charsetName)
Удостоверьтесь, что вы перешли в кодировку как US-ASCII или UTF-8, чтобы быть однозначным.
Просто перейдя на примеры, как и в Реализация, подумайте о том, чтобы поместить это в класс... Каждый раз, когда вы вызываете toString класса, он возвращает следующее значение:
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();
}
}
Этот код должен работать для строки любого размера.
public static String iterateAlphabetic(String input) {
String output = input.toUpperCase();
char[] array = output.toCharArray();
boolean overflow = true;
for(int itr=array.length-1; itr>=0; itr--) {
if(overflow && array[itr]=='Z') {
array[itr] = 'A';
overflow = true;
continue;
}
if(overflow) {
array[itr] = next(alphabeticUpper,array[itr]);
overflow = false;
continue;
}
break;
}
if(overflow)
output = "A" + new String(array);
else
output = new String(array);
if(output.length() < input.length())
output = StringUtil.padding(output, 'A', input.length());
return output;
}
public static String padding(String input, char pad, int width) {
if (width < 0)
throw new IllegalArgumentException("width must be > 0");
if (width < input.length())
return input;
StringBuilder sb = new StringBuilder();
for(int i = 0;i < (width - input.length()); i++) {
sb.append(pad);
}
sb.append(input);
return sb.toString();
}
Основываясь на решении от @cyberz, следующий код является примером того, как вы могли бы написать рекурсивный вызов, который может быть оптимизирован компилятором, который поддерживает Tail Recursion.
Код написан в Groovy, так как он работает на JVM, его синтаксис очень похож на Java, и его компилятор поддерживает оптимизацию хвостовой рекурсии
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)
}
Поскольку ни один из ответов не был полезен для меня, я написал свой собственный код:
/**
* 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;
}