Is there a nicer way of converting a number to its alphabetic equivalent than this?
private String getCharForNumber(int i) {
char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
if (i > 25) {
return null;
}
return Character.toString(alphabet[i]);
}
Maybe something than can deal with numbers greater than 26 more elegantly too?
Just make use of the ASCII representation.
private String getCharForNumber(int i) {
return i > 0 && i < 27 ? String.valueOf((char)(i + 64)) : null;
}
Note: This assumes that i is between 1 and 26 inclusive.
You'll have to change the condition to i > -1 && i < 26 and the increment to 65 if you want i to be zero-based.
Here is the full ASCII table, in case you need to refer to:
Edit:
As some folks suggested here, it's much more readable to directly use the character 'A' instead of its ASCII code.
private String getCharForNumber(int i) {
return i > 0 && i < 27 ? String.valueOf((char)(i + 'A' - 1)) : null;
}
Rather than giving an error or some sentinel value (e.g. '?') for inputs outside of 0-25, I sometimes find it useful to have a well-defined string for all integers. I like to use the following:
0 -> A
1 -> B
2 -> C
...
25 -> Z
26 -> AA
27 -> AB
28 -> AC
...
701 -> ZZ
702 -> AAA
...
This can be extended to negatives as well:
-1 -> -A
-2 -> -B
-3 -> -C
...
-26 -> -Z
-27 -> -AA
...
Java Code:
public static String toAlphabetic(int i) {
if( i<0 ) {
return "-"+toAlphabetic(-i-1);
}
int quot = i/26;
int rem = i%26;
char letter = (char)((int)'A' + rem);
if( quot == 0 ) {
return ""+letter;
} else {
return toAlphabetic(quot-1) + letter;
}
}
Python code, including the ability to use alphanumeric (base 36) or case-sensitive (base 62) alphabets:
def to_alphabetic(i,base=26):
if base < 0 or 62 < base:
raise ValueError("Invalid base")
if i < 0:
return '-'+to_alphabetic(-i-1)
quot = int(i)/base
rem = i%base
if rem < 26:
letter = chr( ord("A") + rem)
elif rem < 36:
letter = str( rem-26)
else:
letter = chr( ord("a") + rem - 36)
if quot == 0:
return letter
else:
return to_alphabetic(quot-1,base) + letter
I would return a character char instead of a string.
public static char getChar(int i) {
return i<0 || i>25 ? '?' : (char)('A' + i);
}
Note: when the character decoder doesn't recognise a character it returns ?
I would use 'A' or 'a' instead of looking up ASCII codes.
Personally, I prefer
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ".substring(i, i+1);
which shares the backing char[]. Alternately, I think the next-most-readable approach is
return Character.toString((char) (i + 'A'));
which doesn't depend on remembering ASCII tables. It doesn't do validation, but if you want to, I'd prefer to write
char c = (char) (i + 'A');
return Character.isUpperCase(c) ? Character.toString(c) : null;
just to make it obvious that you're checking that it's an alphabetic character.
if you define a/A as 0
char res;
if (i>25 || i<0){
res = null;
}
res = (i) + 65
}
return res;
65 for captitals;
97 for non captitals
Getting the alphabetical value from an int can be simply done with:
(char)('#' + i)
public static String abcBase36(int i) {
char[] ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
int quot = i / 36;
int rem = i % 36;
char letter = ALPHABET[rem];
if (quot == 0) {
return "" + letter;
} else {
return abcBase36(quot - 1) + letter;
}
}
You can convert the input to base 26 (Hexavigesimal) and convert each "digit" back to base 10 individually and apply the ASCII mapping. Since A is mapped to 0, you will get results A, B, C,..., Y, Z, BA, BB, BC,...etc, which may or may not be desirable depending on your requirements for input values > 26, since it may be natural to think AA comes after Z.
public static String getCharForNumber(int i){
// return null for bad input
if(i < 0){
return null;
}
// convert to base 26
String s = Integer.toString(i, 26);
char[] characters = s.toCharArray();
String result = "";
for(char c : characters){
// convert the base 26 character back to a base 10 integer
int x = Integer.parseInt(Character.valueOf(c).toString(), 26);
// append the ASCII value to the result
result += String.valueOf((char)(x + 'A'));
}
return result;
}
public static string IntToLetters(int value)
{
string result = string.Empty;
while (--value >= 0)
{
result = (char)('A' + value % 26 ) + result;
value /= 26;
}
return result;
}
To meet the requirement of A being 1 instead of 0, I've added -- to the while loop condition, and removed the value-- from the end of the loop, if anyone wants this to be 0 for their own purposes, you can reverse the changes, or simply add value++; at the beginning of the entire method.
Another variant:
private String getCharForNumber(int i) {
if (i > 25 || i < 0) {
return null;
}
return new Character((char) (i + 65)).toString();
}
This isn't exactly an answer, but some useful/related code I made. When you run it and enter any character in the command line it returns getNumericValue(char) ... which doesn't seem to be the same as the ASCII table so be aware. Anyways, not a direct answer to your question but hopefully helpful:
import java.lang.*;
import java.util.*;
/* charVal.java
*/
//infinite loops. whenever you type in a character gives you value of
//getNumericValue(char)
//
//ctrl+c to exit
public class charVal {
public static void main(String[] args) {
Scanner inPut = new Scanner(System.in);
for(;;){
char c = inPut.next().charAt(0);
System.out.printf("\n %s = %d \n", c, Character.getNumericValue(c));
}
}
}
Another approach starting from 0 and returning a String
public static String getCharForNumber(int i) {
return i < 0 || i > 25 ? "?" : String.valueOf((char) ('A' + i));
}
You can try like this:
private String getCharForNumber(int i) {
CharSequence css = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (i > 25) {
return null;
}
return css.charAt(i) + "";
}
public static void main(String[] args)
{
int rem,n=702,quo;
String s=" ";
while(n>0)
{
rem=(n-1)%26;
quo=(n-1)/26;
s=(char)(rem+97)+s;
if(quo==1)
{
s=(char)(97)+s;
break;
}
else
n=(n-1)/26;
}
System.out.print(s);
}
}
////We can also write the code like the below one. There is no much difference but it may help to understand the concept for some people.
public static void main(String[] args)
{
int rem,n=52,quo;
String s=" ";
while(n>0)
{
rem=n%26;
quo=n/26;
if(rem==0)
rem=26;
s=(char)(rem+96)+s;
if((quo==1 || quo==0) && n>26)
{
n=n/26;
s=(char)(n+96)+s;
break;
}
else
n=n/26-1;
}
System.out.print(s);
}
for(int i=0;i<ar.length();i++) {
char ch = ar.charAt(i);
System.out.println((char)(ch+16));;
}
The accepted answer best describes the solution. Nevertheless, it does not handle the case when you want to keep numbering over the value 27 like aa, ab, ...az, aaa, aab....
For doing so I achieved with the following:
protected String getAlphaNumber(int intValue) {
if (intValue == 0) {
return "";
}
intValue = Math.abs(intValue);
String prefix = "";
int charPosition = intValue % 26;
int blocks = intValue / 26;
if (charPosition == 0) { //the last letter, "z" (26)
blocks--;
charPosition = 26;
}
for (int i = 0; i < blocks; i++) {
prefix += "a";
}
return prefix + ((char) (charPosition + 96)); // "a"-"z" chars are located in the 97-122 indexes of the ASCII table, so shift all 96 positions.
}
import java.util.*;
class Integer2Letter
{
void main()
{
Scanner ob=new Scanner(System.in);
int n;
System.out.println("Enter an Integer");
n=ob.nextInt();//INPUT IN INTEGER FORMAT
if(n>0 && n<27)//RANGE CHECK
{
char ch= (char)(n+64);//IF N=1 THAN 64+1=65 WHICH EQUAL TO THE ASCII VALUE OF 'A'
System.out.println("the corresponding letter is="+ch);
}
else
{
System.out.println("Please enter the correct range");
}
}
}
Related
I have this code which compresses characters in the given string and replaces repeated adjacent characters with their count.
Consider the following example:
Input:
aaabbccdsa
Expecting output:
a3b2c2dsa
My code is working properly but I think repeating if condition can be removed.
public class Solution {
public static String getCompressedString(String str) {
String result = "";
char anch = str.charAt(0);
int count = 0;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch == anch) {
count++;
} else {
if (count == 1) { // from here
result += anch;
} else {
result += anch + Integer.toString(count);
} // to here
anch = ch;
count = 1;
}
if (i == str.length() - 1) {
if (count == 1) { // from here
result += anch;
} else {
result += anch + Integer.toString(count);
} // to here
}
}
return result;
}
}
In this solution code below is repeated two times
if (count == 1) {
result += anch;
} else {
result += anch + Integer.toString(count);
}
Please, note, I don't want to use a separate method for repeating logic.
You could do away with the if statements.
public static String getCompressedString(String str) {
char[] a = str.toCharArray();
StringBuilder sb = new StringBuilder();
for(int i=0,j=0; i<a.length; i=j){
for(j=i+1;j < a.length && a[i] == a[j]; j++);
sb.append(a[i]).append(j-i==1?"":j-i);
}
return sb.toString();
}
}
You can do something like this:
public static String getCompressedString(String str) {
String result = "";
int count = 1;
for (int i = 0; i < str.length(); i++) {
if (i + 1 < str.length() && str.charAt(i) == str.charAt(i + 1)) {
count++;
} else {
if (count == 1) {
result += str.charAt(i);
} else {
result += str.charAt(i) + "" + count;
count = 1;
}
}
}
return result;
}
I got rid of the repeated code, and it do as intended.
You can use this approach as explained below:
Code:
public class Test {
public static void main(String[] args) {
String s = "aaabbccdsaccbbaaadsa";
char[] strArray = s.toCharArray();
char ch0 = strArray[0];
int counter = 0;
StringBuilder sb = new StringBuilder();
for(int i=0;i<strArray.length;i++){
if(ch0 == strArray[i]){//check for consecutive characters and increment the counter
counter++;
} else { // when character changes while iterating
sb.append(ch0 + "" + (counter > 1 ? counter : ""));
counter = 1; // reset the counter to 1
ch0 = strArray[i]; // reset the ch0 with the current character
}
if(i == strArray.length-1){// case for last element of the string
sb.append(ch0 + "" + (counter > 1 ? counter : ""));
}
}
System.out.println(sb);
}
}
Sample Input/Output:
Input:: aaabbccdsaccbbaaadsa
Output:: a3b2c2dsac2b2a3dsa
Input:: abcdaaaaa
Output:: abcda5
Since, the body of the else and second if is the same, so we can merge them by updating the condition. The updated body of the function will be:
String result = "";
char anch = str.charAt(0);
int count = 0;
char ch = str.charAt(0); // declare ch outside the loop, and initialize to avoid error
for (int i = 0; i < str.length(); i++) {
ch = str.charAt(i);
if (ch == anch) {
count++;
}
// check if the second condition is false, or if we are at the end of the string
if (ch != anch || i == str.length() - 1) {
if (count == 1) { // from here
result += anch;
} else {
result += anch + Integer.toString(count);
} // to here
anch = ch;
count = 1;
}
}
// add the condition
// if count is greater than or
// if the last character added already to the result
if (count > 1 || (len < 2 || result.charAt(len - 2) != ch)) {
result += ch;
}
return result;
Test Cases:
I have tested the solution on the following inputs:
aaabbccdsa -> a3b2c2dsa
aaab -> a3b
aaa -> a3
ab -> ab
aabbc -> a2b2c
Optional
If you want to make it shorter, you can update these 2 conditions.
if (count == 1) { // from here
result += anch;
} else {
result += anch + Integer.toString(count);
} // to here
as
result += anch;
if (count != 1) { // from here
result += count;// no need to convert (implicit conversion)
} // to here
Here's a single-statement solution using Stream API and regular expressions:
public static final Pattern GROUP_OF_ONE_OR_MORE = Pattern.compile("(.)\\1*");
public static String getCompressedString(String str) {
return GROUP_OF_ONE_OR_MORE.matcher(str).results()
.map(MatchResult::group)
.map(s -> s.charAt(0) + (s.length() == 1 ? "" : String.valueOf(s.length())))
.collect(Collectors.joining());
}
main()
public static void main(String[] args) {
System.out.println(getCompressedString("aaabbccdsa"));
System.out.println(getCompressedString("awswwwhhhp"));
}
Output:
a3b2c2dsa // "aaabbccdsa"
awsw3h3p // "awswwwhhhp"
How does it work
A regular expression "(.)\\1*" is capturing a group (.) of identical characters of length 1 or greater. Where . - denotes any symbol, and \\1 is a back reference to the group.
Method Matcher.results() "returns a stream of match results for each subsequence of the input sequence that matches the pattern".
The only thing left is to evaluate the length of each group and transform it accordingly before collecting into the resulting String.
Links:
A quick tutorial on Regular Expressions.
Official tutorials on lambda expressions and streams
You can use a function which has the following 3 parameters : result, anch, count .
something of this sort:
private static String extractedFunction(String result,int count, char anch) {
return count ==1 ? (result + anch) : (result +anch+Integer.toString(count) );
}
make a function call from those two points like this :
result = extractedFunction(result,count,anch);
Try this.
static final Pattern PAT = Pattern.compile("(.)\\1*");
static String getCompressedString(String str) {
return PAT.matcher(str)
.replaceAll(m -> m.group(1)
+ (m.group().length() == 1 ? "" : m.group().length()));
}
Test cases:
#Test
public void testGetCompressedString() {
assertEquals("", getCompressedString(""));
assertEquals("a", getCompressedString("a"));
assertEquals("abc", getCompressedString("abc"));
assertEquals("abc3", getCompressedString("abccc"));
assertEquals("a3b2c2dsa", getCompressedString("aaabbccdsa"));
}
The regular expression "(.)\\1*" used here matches any sequence of identical characters. .replaceAll() takes a lambda expression as an argument, evaluates the lambda expression each time the pattern matches, and replaces the original string with the result.
The lambda expression is passed a Matcher object containing the results of the match. Here we are receiving this object in the variable m. m.group() returns the entire matched substring, m.group(1) returns its first character.
If the input string is "aaabbccdsa", it will be processed as follows.
m.group(1) m.group() returned by lambda
a aaa a3
b bb b2
c cc c2
d d d
s s s
a a a
Given a random character string not including (0-9), I need to shorten the representation of that string by adding the number of consecutive characters. For e.g: ggee will result in g2e2 being displayed.
I managed to implement the program and tested it (works correctly) through various inputs. I have run into the issue where I cannot seem to understand how the character "e" is displayed given the input above.
I have traced my code multiple times but I don't see when/how "e" is displayed when "i" is 2/3.
String input = new String("ggee");
char position = input.charAt(0);
int accumulator = 1;
for (int i = 1; i < input.length(); i++)
{
// Correction. Was boolean lastIndexString = input.charAt(i) == (input.charAt(input.length() - 1));
boolean lastIndexString = i == (input.length() - 1);
if (position == input.charAt(i))
{
accumulator++;
if (lastIndexOfString)
System.out.print(accumulator); // In my mind, I should be printing
// (input.charAt(i) + "" + accumulator); here
}
else //(position != input.charAt(i))
{
if (accumulator > 1)
{
System.out.print(position + "" + accumulator);
}
else
{
System.out.print(position + "");
}
position = input.charAt(i);
accumulator = 1;
if (lastIndexOfString)
System.out.print(input.charAt(i)); // This is always printing when
// I am at the last index of my string,
// even ignoring my condition of
// (position == input.charAt(i))
}
}
In Java 9+, using regular expression to find consecutive characters, the following will do it:
static String shorten(String input) {
return Pattern.compile("(.)\\1+").matcher(input)
.replaceAll(r -> r.group(1) + r.group().length());
}
Test
System.out.println(shorten("ggggeecaaaaaaaaaaaa"));
System.out.println(shorten("ggggee😀😀😀😁😁😁😁"));
Output
g4e2ca12
g4e2😀6😁8
However, as you can see, that code doesn't work if input string contains Unicode characters from the supplemental planes, such as Emoji characters.
Small modification will fix that:
static String shorten(String input) {
return Pattern.compile("(.)\\1+").matcher(input)
.replaceAll(r -> r.group(1) + r.group().codePointCount(0, r.group().length()));
}
Or:
static String shorten(String input) {
return Pattern.compile("(.)\\1+").matcher(input)
.replaceAll(r -> r.group(1) + input.codePointCount(r.start(), r.end()));
}
Output
g4e2ca12
g4e2😀3😁4
Basically you want each char with no of repeats.
*******************************************************************************/
public class Main
{
public static void main(String[] args) {
String s="ggggggeee";
StringBuilder s1=new
StringBuilder("") ;
;
for(int i=0;i<s.length();i++)
{
int count=0,j;
for( j=i+1;j<s.length();j++)
{
if(s.charAt(i)==s.charAt(j))
count++;
else
{
break;}
}
i=j-1;
s1=s1.append(s.charAt(i)+""+(count+1));
}
System.out.print(s1);
}}
Output
I have this assignment that needs me to decompress a previously compressed string.
Examples of this would be
i4a --> iaaaa
q3w2ai2b --> qwwwaaibb
3a --> aaa
Here's what I've written so far:
public static String decompress(String compressedText)
{
char c;
char let;
int num;
String done = "";
String toBeDone = "";
String toBeDone2 = "";
if(compressedText.length() <= 1)
{
return compressedText;
}
if (Character.isLetter(compressedText.charAt(0)))
{
done = compressedText.substring(0,1);
toBeDone = compressedText.substring(1);
return done + decompress(toBeDone);
}
else
{
c = compressedText.charAt(0);
num = Character.getNumericValue(c);
let = compressedText.charAt(1);
if (num > 0)
{
num--;
toBeDone = num + Character.toString(let);
toBeDone2 = compressedText.substring(2);
return Character.toString(let) + decompress(toBeDone) + decompress(toBeDone2);
}
else
{
toBeDone2 = compressedText.substring(2);
return Character.toString(let) + decompress(toBeDone2);
}
}
}
My return values are absolutely horrendous.
"ab" yields "babb" somehow.
"a" or any 1 letter string string yields the right result
"2a" yields "aaaaaaaaaaa"
"2a3b" gives me "aaaabbbbbbbbbbbbbbbbbbbbbbbbbbaaabbbbaaaabbbbbbbbbbbbbbbbbbbbbbbbbb"
The only place I can see a mistake in would probably be the last else section, since I wasn't entirely sure on what to do once the number reaches 0 and I have to stop using recursion on the letter after it. Other than that, I can't really see a problem that gives such horrifying outputs.
I reckon something like this would work:
public static String decompress(String compressedText) {
if (compressedText.length() <= 1) {
return compressedText;
}
char c = compressedText.charAt(0);
if (Character.isDigit(c)) {
return String.join("", Collections.nCopies(Character.digit(c, 10), compressedText.substring(1, 2))) + decompress(compressedText.substring(2));
}
return compressedText.charAt(0) + decompress(compressedText.substring(1));
}
As you can see, the base case is when the compressed String has a length less than or equal to 1 (as you have it in your program).
Then, we check if the first character is a digit. If so, we substitute in the correct amount of characters, and continue with the recursive process until we reach the base case.
If the first character is not a digit, then we simply append it and continue.
Keep in mind that this will only work with numbers from 1 to 9; if you require higher values, let me know!
EDIT 1: If the Collections#nCopies method is too complex, here is an equivalent method:
if (Character.isDigit(c)) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Character.digit(c, 10); i++) {
sb.append(compressedText.charAt(1));
}
return sb.toString() + decompress(compressedText.substring(2));
}
EDIT 2: Here is a method that uses a recursive helper-method to repeat a String:
public static String decompress(String compressedText) {
if (compressedText.length() <= 1) {
return compressedText;
}
char c = compressedText.charAt(0);
if (Character.isDigit(c)) {
return repeatCharacter(compressedText.charAt(1), Character.digit(c, 10)) + decompress(compressedText.substring(2));
}
return compressedText.charAt(0) + decompress(compressedText.substring(1));
}
public static String repeatCharacter(char character, int counter) {
if (counter == 1) {
return Character.toString(character);
}
return character + repeatCharacter(character, counter - 1);
}
In my program I need to insert - between two odd numbers and * between even numbers and ignore if there is 0. For example:
Input = 99946 Output = 9-9-94*6
Input = 56647304 Output = 56*6*47-304
Method getDigits() places the digits of the entered number into array cells. Method insertDashesAsteriks() returns properly concatenated String.
But when I run my program with the following example:
Please enter the numbers so they could be rearranged:
222234411110000
Exception in thread "main" java.util.InputMismatchException: For input string: "222234411110000"
at java.util.Scanner.nextInt(Unknown Source)
at java.util.Scanner.nextInt(Unknown Source)
at DashInsert2.main(DashInsert2.java:9)
then I'm getting InputMismatchException. Why am I getting the error?
import java.util.Scanner;
public class DashInsert2 {
public static void main(String[] args)
{
Scanner kbd = new Scanner(System.in);
System.out.println("Please enter the numbers so they could be rearranged: ");
int nums = kbd.nextInt();
int[] numArray = getDigits(nums);
System.out.println("The array representation of the numbers is \n");
System.out.println();
String result = insertDashesAsteriks(numArray);
System.out.println("The result is " + result);
}
public static int[] getDigits(int numbers)
{
int length = Integer.toString(numbers).length();
int[] temp = new int[length];
for(int i = 0; i < length; i++)
{
temp[i] = numbers % 10;
numbers = numbers / 10;
}
return temp;
}
public static String insertDashesAsteriks(int[] numArray)
{
String temp = "";
for(int i = 1; i < numArray.length; i++)
{
if(numArray[i] % 2 == 0 && numArray[i-1] % 2 ==0)
{
temp = numArray[i-1] + "*" + numArray[i] + "*";
}
else if(numArray[i] == 0 || numArray[i-1] == 0)
{
temp = numArray[i-1] + "" + numArray[i] + "";
}
else if(numArray[i] % 2 != 0 && numArray[i-1] % 2 != 0)
{
temp = numArray[i-1] + "-" + numArray[i] + "-";
}
}
return temp;
}
}
Maximum value for int is 2,147,483,647
You entered: 222,234,411,110,000
You'll need to treat the number as a string since the number you input is past the biggest possible 32 bit integer.
Try kbd.next().charAt(0); to parse it character by character instead.
First off, if you're reading in ints, you're limited to their range. That means numbers beyond about +/- two billion are out of the question. For handling larger number, you can move to larger data types (like long) or just handle strings, which have far less stringent limitations.
Once you are handling strings, there's a far simpler way (in terms of the code you have to write) to do this substitution using regular expressions:
public class Test {
static String morph(String s) {
String oldS;
do {
oldS = s;
s = s.replaceAll("([13579])([13579])", "$1-$2");
s = s.replaceAll("([2468])([2468])", "$1*$2");
} while (! s.equals(oldS));
return s;
}
public static void main(String args[]){
System.out.println(morph("99946"));
System.out.println(morph("56647304"));
System.out.println(morph("222234411110000"));
}
}
The morph function simply modifies the string with your substitution rules until it ceases to change. The output of the test harness (using the data you supplied) is:
9-9-94*6
56*6*47-304
2*2*2*234*41-1-1-10000
Now it may be that, if this is a classwork assignment, you're limited in the language facilities you can use. But, since you haven't mentioned that, and no coder in their right mind would (usually) choose a more difficult path, you should consider the use of the regular expression method. Code that is shorter is almost always less prone to bugs.
If you don't want to use regular expressions, you can still make your code relatively short and well structured, with something like:
// Helper functions for inserting characters.
static boolean is2468 (char ch) {
return (ch == '2' || ch == '4' || ch == '6' || ch == '8');
}
static boolean is13579 (char ch) {
return (ch == '1' || ch == '3' || ch == '5' || ch == '7' || ch == '9');
}
static String morph(String str) {
// Use efficient string builder for creating morphed string.
StringBuilder newStr = new StringBuilder();
// Last/current character, starting with '0' simplifies
// start condition.
char lastCh, ch = '0';
// Process every character in string.
for (int idx = 0; idx < str.length(); idx++) {
// Transfer previous current to last, get current.
lastCh = ch;
ch = str.charAt(idx);
// Put '-' between two odds, '*' between two non-zero evens.
if (is13579(lastCh) && is13579(ch))
newStr.append('-');
else if (is2468(lastCh) && is2468(ch))
newStr.append('*');
// Put character there regardless.
newStr.append(ch);
}
// Return string version of string builder.
return newStr.toString();
}
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?
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.
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.
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';
}
}
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;
}
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);
}
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...
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.
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();
}
}
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)
}
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;
}
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
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);
}
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);
}
}
}
}