How to efficiently form array of suffixes? - java

I was looking for approach make array of suffixes at Java.
I found two ability variants. Moreover I want much more deeply understand differents between this variants.
Includes running time & space.
Code (suffixes):
public static String[] suffixes(String s)
{
int N = s.length();
String[] suffixes = new String[N];
for (int i = 0; i < N; i++)
suffixes[i] = s.substring(i, N);
return suffixes;
}
Code (StringBuilder suffixes):
public static String[] suffixes(String s)
{
int N = s.length();
StringBuilder sb = new StringBuilder(s);
String[] suffixes = new String[N];
for (int i = 0; i < N; i++)
suffixes[i] = sb.substring(i, N);
return suffixes;
}
Question:
How to efficiently form array of suffixes?

There will be no discernable difference between the two ways of doing it that you describe: since Strings in Java are immutable, a new object will be created for each suffix. Making a substring from a String vs. StringBuilder will not give you much difference in performance, compared to allocations and copying required to set up the new string objects.
When you are looking for a suffix, passing the end index is not necessary: use the overload that takes a single int instead:
for (int i = 0; i < N; i++)
suffixes[i] = s.substring(i);

The most efficient way would be to use a char array. However, it won't be so significant as the most costy operation is creating the String objects.
String s = "foobarbaz";
char[] cha = s.toCharArray();
int length = cha.length;
String[] suffixes = new String[length];
for (int i = 0; i < length; ++i)
suffixes[i] = new String(cha, i, length-i);

You could do this, which avoids the substring method,
public String[] suffix(String s)
{
String[] suffixes = new String[s.length()];
String suffix = null;
for (int i = 0 ; i < s.length() ; i++)
{
suffix = suffix == null ? "" + s.charAt(i) : suffix + s.charAt(i);
suffixes[i] = suffix;
}
return suffixes;
}
not sure if it is any faster though.

The only difference between your code snippets is using String or StringBuilder, also you are using it only to retrieve sub string.
subString() from StringBuilder does
new String(offset + beginIndex, endIndex - beginIndex, value);
and subString() from String does
new String(offset + beginIndex, endIndex - beginIndex, value);
both are same and creates new String so there wont be any difference in performance

At the end you always require n + 1 string to complete this task. Only thing that can be optimized is the time when those objects are created.
You could create the string representation as char array and lazy (on demand) return the suffixes.
You can use Iterable and Iterator interfaces to do that:
public class StringSufixies implements Iterable<String> {
private final String input;
public StringSufixies(String input) {
this.input = input;
}
#Override
public Iterator<String> iterator() {
return new SuffixStringIterator(input);
}
private static class SuffixStringIterator implements Iterator<String> {
private final String input;
private final int size;
private int suffixId;
private SuffixStringIterator(String input) {
this.input = input;
this.size = input.length();
this.suffixId = 1;
}
#Override
public boolean hasNext() {
return suffixId <= size;
}
#Override
public String next() {
return input.substring(0, suffixId++); //At this point we create new String
}
#Override
public void remove() {
//Add throw or other impl
}
}
}
You could implement the key functionality over a char array.
private static class SuffixCharIterator implements Iterator<String> {
private final char[] charSequence;
private final int size;
private int suffixId = 0;
private SuffixCharIterator(char[] charSequence) {
this.charSequence = charSequence;
this.size = charSequence.length;
}
#Override
public boolean hasNext() {
return suffixId <= size;
}
#Override
public String next() {
return new String(charSequence, 0, suffixId++); //At this point we create a new String
}
#Override
public void remove() {
}
}
But IMHO is more complex and we do not gain nothing.
The advantage of this solution is that you can work on results and decide to stop before all prefixes are created.

Related

The method repeat (int ) is indefined for string type [duplicate]

I'm looking for a simple commons method or operator that allows me to repeat some string n times. I know I could write this using a for loop, but I wish to avoid for loops whenever necessary and a simple direct method should exist somewhere.
String str = "abc";
String repeated = str.repeat(3);
repeated.equals("abcabcabc");
Related to:
repeat string javascript
Create NSString by repeating another string a given number of times
Edited
I try to avoid for loops when they are not completely necessary because:
They add to the number of lines of code even if they are tucked away in another function.
Someone reading my code has to figure out what I am doing in that for loop. Even if it is commented and has meaningful variables names, they still have to make sure it is not doing anything "clever".
Programmers love to put clever things in for loops, even if I write it to "only do what it is intended to do", that does not preclude someone coming along and adding some additional clever "fix".
They are very often easy to get wrong. For loops involving indexes tend to generate off by one bugs.
For loops often reuse the same variables, increasing the chance of really hard to find scoping bugs.
For loops increase the number of places a bug hunter has to look.
Here is the shortest version (Java 1.5+ required):
repeated = new String(new char[n]).replace("\0", s);
Where n is the number of times you want to repeat the string and s is the string to repeat.
No imports or libraries needed.
If you are using Java <= 7, this is as "concise" as it gets:
// create a string made up of n copies of string s
String.format("%0" + n + "d", 0).replace("0", s);
In Java 8 and above there is a more readable way:
// create a string made up of n copies of string s
String.join("", Collections.nCopies(n, s));
Finally, for Java 11 and above, there is a new repeat​(int count) method specifically for this purpose(link)
"abc".repeat(12);
Alternatively, if your project uses java libraries there are more options.
For Apache Commons:
StringUtils.repeat("abc", 12);
For Google Guava:
Strings.repeat("abc", 12);
String::repeat
". ".repeat(7) // Seven period-with-space pairs: . . . . . . .
New in Java 11 is the method String::repeat that does exactly what you asked for:
String str = "abc";
String repeated = str.repeat(3);
repeated.equals("abcabcabc");
Its Javadoc says:
/**
* Returns a string whose value is the concatenation of this
* string repeated {#code count} times.
* <p>
* If this string is empty or count is zero then the empty
* string is returned.
*
* #param count number of times to repeat
*
* #return A string composed of this string repeated
* {#code count} times or the empty string if this
* string is empty or count is zero
*
* #throws IllegalArgumentException if the {#code count} is
* negative.
*
* #since 11
*/
Commons Lang StringUtils.repeat()
Usage:
String str = "abc";
String repeated = StringUtils.repeat(str, 3);
repeated.equals("abcabcabc");
Java 8's String.join provides a tidy way to do this in conjunction with Collections.nCopies:
// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));
Here's a way to do it using only standard String functions and no explicit loops:
// create a string made up of n copies of s
repeated = String.format(String.format("%%%ds", n), " ").replace(" ",s);
If you're like me and want to use Google Guava and not Apache Commons. You can use the repeat method in the Guava Strings class.
Strings.repeat("-", 60);
With java-8, you can also use Stream.generate.
import static java.util.stream.Collectors.joining;
...
String repeated = Stream.generate(() -> "abc").limit(3).collect(joining()); //"abcabcabc"
and you can wrap it in a simple utility method if needed:
public static String repeat(String str, int times) {
return Stream.generate(() -> str).limit(times).collect(joining());
}
So you want to avoid loops?
Here you have it:
public static String repeat(String s, int times) {
if (times <= 0) return "";
else return s + repeat(s, times-1);
}
(of course I know this is ugly and inefficient, but it doesn't have loops :-p)
You want it simpler and prettier? use jython:
s * 3
Edit: let's optimize it a little bit :-D
public static String repeat(String s, int times) {
if (times <= 0) return "";
else if (times % 2 == 0) return repeat(s+s, times/2);
else return s + repeat(s+s, times/2);
}
Edit2: I've done a quick and dirty benchmark for the 4 main alternatives, but I don't have time to run it several times to get the means and plot the times for several inputs... So here's the code if anybody wants to try it:
public class Repeat {
public static void main(String[] args) {
int n = Integer.parseInt(args[0]);
String s = args[1];
int l = s.length();
long start, end;
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
if(repeatLog2(s,i).length()!=i*l) throw new RuntimeException();
}
end = System.currentTimeMillis();
System.out.println("RecLog2Concat: " + (end-start) + "ms");
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
if(repeatR(s,i).length()!=i*l) throw new RuntimeException();
}
end = System.currentTimeMillis();
System.out.println("RecLinConcat: " + (end-start) + "ms");
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
if(repeatIc(s,i).length()!=i*l) throw new RuntimeException();
}
end = System.currentTimeMillis();
System.out.println("IterConcat: " + (end-start) + "ms");
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
if(repeatSb(s,i).length()!=i*l) throw new RuntimeException();
}
end = System.currentTimeMillis();
System.out.println("IterStrB: " + (end-start) + "ms");
}
public static String repeatLog2(String s, int times) {
if (times <= 0) {
return "";
}
else if (times % 2 == 0) {
return repeatLog2(s+s, times/2);
}
else {
return s + repeatLog2(s+s, times/2);
}
}
public static String repeatR(String s, int times) {
if (times <= 0) {
return "";
}
else {
return s + repeatR(s, times-1);
}
}
public static String repeatIc(String s, int times) {
String tmp = "";
for (int i = 0; i < times; i++) {
tmp += s;
}
return tmp;
}
public static String repeatSb(String s, int n) {
final StringBuilder sb = new StringBuilder();
for(int i = 0; i < n; i++) {
sb.append(s);
}
return sb.toString();
}
}
It takes 2 arguments, the first is the number of iterations (each function run with repeat times arg from 1..n) and the second is the string to repeat.
So far, a quick inspection of the times running with different inputs leaves the ranking something like this (better to worse):
Iterative StringBuilder append (1x).
Recursive concatenation log2 invocations (~3x).
Recursive concatenation linear invocations (~30x).
Iterative concatenation linear (~45x).
I wouldn't ever guessed that the recursive function was faster than the for loop :-o
Have fun(ctional xD).
This contains less characters than your question
public static String repeat(String s, int n) {
if(s == null) {
return null;
}
final StringBuilder sb = new StringBuilder(s.length() * n);
for(int i = 0; i < n; i++) {
sb.append(s);
}
return sb.toString();
}
based on fortran's answer, this is a recusive version that uses a StringBuilder:
public static void repeat(StringBuilder stringBuilder, String s, int times) {
if (times > 0) {
repeat(stringBuilder.append(s), s, times - 1);
}
}
public static String repeat(String s, int times) {
StringBuilder stringBuilder = new StringBuilder(s.length() * times);
repeat(stringBuilder, s, times);
return stringBuilder.toString();
}
using Dollar is simple as typing:
#Test
public void repeatString() {
String string = "abc";
assertThat($(string).repeat(3).toString(), is("abcabcabc"));
}
PS: repeat works also for array, List, Set, etc
I wanted a function to create a comma-delimited list of question marks for JDBC purposes, and found this post. So, I decided to take two variants and see which one performed better. After 1 million iterations, the garden-variety StringBuilder took 2 seconds (fun1), and the cryptic supposedly more optimal version (fun2) took 30 seconds. What's the point of being cryptic again?
private static String fun1(int size) {
StringBuilder sb = new StringBuilder(size * 2);
for (int i = 0; i < size; i++) {
sb.append(",?");
}
return sb.substring(1);
}
private static String fun2(int size) {
return new String(new char[size]).replaceAll("\0", ",?").substring(1);
}
OOP Solution
Nearly every answer proposes a static function as a solution, but thinking Object-Oriented (for reusability-purposes and clarity) I came up with a Solution via Delegation through the CharSequence-Interface (which also opens up usability on mutable CharSequence-Classes).
The following Class can be used either with or without Separator-String/CharSequence and each call to "toString()" builds the final repeated String.
The Input/Separator are not only limited to String-Class, but can be every Class which implements CharSequence (e.g. StringBuilder, StringBuffer, etc)!
Source-Code:
/**
* Helper-Class for Repeating Strings and other CharSequence-Implementations
* #author Maciej Schuttkowski
*/
public class RepeatingCharSequence implements CharSequence {
final int count;
CharSequence internalCharSeq = "";
CharSequence separator = "";
/**
* CONSTRUCTOR - RepeatingCharSequence
* #param input CharSequence to repeat
* #param count Repeat-Count
*/
public RepeatingCharSequence(CharSequence input, int count) {
if(count < 0)
throw new IllegalArgumentException("Can not repeat String \""+input+"\" less than 0 times! count="+count);
if(count > 0)
internalCharSeq = input;
this.count = count;
}
/**
* CONSTRUCTOR - Strings.RepeatingCharSequence
* #param input CharSequence to repeat
* #param count Repeat-Count
* #param separator Separator-Sequence to use
*/
public RepeatingCharSequence(CharSequence input, int count, CharSequence separator) {
this(input, count);
this.separator = separator;
}
#Override
public CharSequence subSequence(int start, int end) {
checkBounds(start);
checkBounds(end);
int subLen = end - start;
if (subLen < 0) {
throw new IndexOutOfBoundsException("Illegal subSequence-Length: "+subLen);
}
return (start == 0 && end == length()) ? this
: toString().substring(start, subLen);
}
#Override
public int length() {
//We return the total length of our CharSequences with the separator 1 time less than amount of repeats:
return count < 1 ? 0
: ( (internalCharSeq.length()*count) + (separator.length()*(count-1)));
}
#Override
public char charAt(int index) {
final int internalIndex = internalIndex(index);
//Delegate to Separator-CharSequence or Input-CharSequence depending on internal index:
if(internalIndex > internalCharSeq.length()-1) {
return separator.charAt(internalIndex-internalCharSeq.length());
}
return internalCharSeq.charAt(internalIndex);
}
#Override
public String toString() {
return count < 1 ? ""
: new StringBuilder(this).toString();
}
private void checkBounds(int index) {
if(index < 0 || index >= length())
throw new IndexOutOfBoundsException("Index out of Bounds: "+index);
}
private int internalIndex(int index) {
// We need to add 1 Separator-Length to total length before dividing,
// as we subtracted one Separator-Length in "length()"
return index % ((length()+separator.length())/count);
}
}
Usage-Example:
public static void main(String[] args) {
//String input = "12345";
//StringBuffer input = new StringBuffer("12345");
StringBuilder input = new StringBuilder("123");
//String separator = "<=>";
StringBuilder separator = new StringBuilder("<=");//.append('>');
int repeatCount = 2;
CharSequence repSeq = new RepeatingCharSequence(input, repeatCount, separator);
String repStr = repSeq.toString();
System.out.println("Repeat="+repeatCount+"\tSeparator="+separator+"\tInput="+input+"\tLength="+input.length());
System.out.println("CharSeq:\tLength="+repSeq.length()+"\tVal="+repSeq);
System.out.println("String :\tLength="+repStr.length()+"\tVal="+repStr);
//Here comes the Magic with a StringBuilder as Input, as you can append to the String-Builder
//and at the same Time your Repeating-Sequence's toString()-Method returns the updated String :)
input.append("ff");
System.out.println(repSeq);
//Same can be done with the Separator:
separator.append("===").append('>');
System.out.println(repSeq);
}
Example-Output:
Repeat=2 Separator=<= Input=123 Length=3
CharSeq: Length=8 Val=123<=123
String : Length=8 Val=123<=123
123ff<=123ff
123ff<====>123ff
using only JRE classes (System.arraycopy) and trying to minimize the number of temp objects you can write something like:
public static String repeat(String toRepeat, int times) {
if (toRepeat == null) {
toRepeat = "";
}
if (times < 0) {
times = 0;
}
final int length = toRepeat.length();
final int total = length * times;
final char[] src = toRepeat.toCharArray();
char[] dst = new char[total];
for (int i = 0; i < total; i += length) {
System.arraycopy(src, 0, dst, i, length);
}
return String.copyValueOf(dst);
}
EDIT
and without loops you can try with:
public static String repeat2(String toRepeat, int times) {
if (toRepeat == null) {
toRepeat = "";
}
if (times < 0) {
times = 0;
}
String[] copies = new String[times];
Arrays.fill(copies, toRepeat);
return Arrays.toString(copies).
replace("[", "").
replace("]", "").
replaceAll(", ", "");
}
EDIT 2
using Collections is even shorter:
public static String repeat3(String toRepeat, int times) {
return Collections.nCopies(times, toRepeat).
toString().
replace("[", "").
replace("]", "").
replaceAll(", ", "");
}
however I still like the first version.
Not the shortest, but (i think) the fastest way is to use the StringBuilder:
/**
* Repeat a String as many times you need.
*
* #param i - Number of Repeating the String.
* #param s - The String wich you want repeated.
* #return The string n - times.
*/
public static String repeate(int i, String s) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < i; j++)
sb.append(s);
return sb.toString();
}
If speed is your concern, then you should use as less memory copying as possible. Thus it is required to work with arrays of chars.
public static String repeatString(String what, int howmany) {
char[] pattern = what.toCharArray();
char[] res = new char[howmany * pattern.length];
int length = pattern.length;
for (int i = 0; i < howmany; i++)
System.arraycopy(pattern, 0, res, i * length, length);
return new String(res);
}
To test speed, a similar optimal method using StirngBuilder is like this:
public static String repeatStringSB(String what, int howmany) {
StringBuilder out = new StringBuilder(what.length() * howmany);
for (int i = 0; i < howmany; i++)
out.append(what);
return out.toString();
}
and the code to test it:
public static void main(String... args) {
String res;
long time;
for (int j = 0; j < 1000; j++) {
res = repeatString("123", 100000);
res = repeatStringSB("123", 100000);
}
time = System.nanoTime();
res = repeatString("123", 1000000);
time = System.nanoTime() - time;
System.out.println("elapsed repeatString: " + time);
time = System.nanoTime();
res = repeatStringSB("123", 1000000);
time = System.nanoTime() - time;
System.out.println("elapsed repeatStringSB: " + time);
}
And here the run results from my system:
elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937
Note that the test for loop is to kick in JIT and have optimal results.
a straightforward one-line solution:
requires Java 8
Collections.nCopies( 3, "abc" ).stream().collect( Collectors.joining() );
for the sake of readability and portability:
public String repeat(String str, int count){
if(count <= 0) {return "";}
return new String(new char[count]).replace("\0", str);
}
If you are worried about performance, just use a StringBuilder inside the loop and do a .toString() on exit of the Loop. Heck, write your own Util Class and reuse it. 5 Lines of code max.
I really enjoy this question. There is a lot of knowledge and styles. So I can't leave it without show my rock and roll ;)
{
String string = repeat("1234567890", 4);
System.out.println(string);
System.out.println("=======");
repeatWithoutCopySample(string, 100000);
System.out.println(string);// This take time, try it without printing
System.out.println(string.length());
}
/**
* The core of the task.
*/
#SuppressWarnings("AssignmentToMethodParameter")
public static char[] repeat(char[] sample, int times) {
char[] r = new char[sample.length * times];
while (--times > -1) {
System.arraycopy(sample, 0, r, times * sample.length, sample.length);
}
return r;
}
/**
* Java classic style.
*/
public static String repeat(String sample, int times) {
return new String(repeat(sample.toCharArray(), times));
}
/**
* Java extreme memory style.
*/
#SuppressWarnings("UseSpecificCatch")
public static void repeatWithoutCopySample(String sample, int times) {
try {
Field valueStringField = String.class.getDeclaredField("value");
valueStringField.setAccessible(true);
valueStringField.set(sample, repeat((char[]) valueStringField.get(sample), times));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
Do you like it?
public static String repeat(String str, int times) {
int length = str.length();
int size = length * times;
char[] c = new char[size];
for (int i = 0; i < size; i++) {
c[i] = str.charAt(i % length);
}
return new String(c);
}
Simple loop
public static String repeat(String string, int times) {
StringBuilder out = new StringBuilder();
while (times-- > 0) {
out.append(string);
}
return out.toString();
}
Try this out:
public static char[] myABCs = {'a', 'b', 'c'};
public static int numInput;
static Scanner in = new Scanner(System.in);
public static void main(String[] args) {
System.out.print("Enter Number of Times to repeat: ");
numInput = in.nextInt();
repeatArray(numInput);
}
public static int repeatArray(int y) {
for (int a = 0; a < y; a++) {
for (int b = 0; b < myABCs.length; b++) {
System.out.print(myABCs[b]);
}
System.out.print(" ");
}
return y;
}
Using recursion, you can do the following (using ternary operators, one line max):
public static final String repeat(String string, long number) {
return number == 1 ? string : (number % 2 == 0 ? repeat(string + string, number / 2) : string + repeat(string + string, (number - 1) / 2));
}
I know, it's ugly and probably not efficient, but it's one line!
If you only know the length of the output string (and it may be not divisible by the length of the input string), then use this method:
static String repeat(String s, int length) {
return s.length() >= length ? s.substring(0, length) : repeat(s + s, length);
}
Usage demo:
for (int i = 0; i < 50; i++)
System.out.println(repeat("_/‾\\", i));
Don't use with empty s and length > 0, since it's impossible to get the desired result in this case.
Despite your desire not to use loops, I think you should use a loop.
String repeatString(String s, int repetitions)
{
if(repetitions < 0) throw SomeException();
else if(s == null) return null;
StringBuilder stringBuilder = new StringBuilder(s.length() * repetitions);
for(int i = 0; i < repetitions; i++)
stringBuilder.append(s);
return stringBuilder.toString();
}
Your reasons for not using a for loop are not good ones. In response to your criticisms:
Whatever solution you use will almost certainly be longer than this. Using a pre-built function only tucks it under more covers.
Someone reading your code will have to figure out what you're doing in that non-for-loop. Given that a for-loop is the idiomatic way to do this, it would be much easier to figure out if you did it with a for loop.
Yes someone might add something clever, but by avoiding a for loop you are doing something clever. That's like shooting yourself in the foot intentionally to avoid shooting yourself in the foot by accident.
Off-by-one errors are also mind-numbingly easy to catch with a single test. Given that you should be testing your code, an off-by-one error should be easy to fix and catch. And it's worth noting: the code above does not contain an off-by-one error. For loops are equally easy to get right.
So don't reuse variables. That's not the for-loop's fault.
Again, so does whatever solution you use. And as I noted before; a bug hunter will probably be expecting you to do this with a for loop, so they'll have an easier time finding it if you use a for loop.
here is the latest Stringutils.java StringUtils.java
public static String repeat(String str, int repeat) {
// Performance tuned for 2.0 (JDK1.4)
if (str == null) {
return null;
}
if (repeat <= 0) {
return EMPTY;
}
int inputLength = str.length();
if (repeat == 1 || inputLength == 0) {
return str;
}
if (inputLength == 1 && repeat <= PAD_LIMIT) {
return repeat(str.charAt(0), repeat);
}
int outputLength = inputLength * repeat;
switch (inputLength) {
case 1 :
return repeat(str.charAt(0), repeat);
case 2 :
char ch0 = str.charAt(0);
char ch1 = str.charAt(1);
char[] output2 = new char[outputLength];
for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
output2[i] = ch0;
output2[i + 1] = ch1;
}
return new String(output2);
default :
StringBuilder buf = new StringBuilder(outputLength);
for (int i = 0; i < repeat; i++) {
buf.append(str);
}
return buf.toString();
}
}
it doesn't even need to be this big, can be made into this, and can be copied and pasted
into a utility class in your project.
public static String repeat(String str, int num) {
int len = num * str.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < times; i++) {
sb.append(str);
}
return sb.toString();
}
So e5, I think the best way to do this would be to simply use the above mentioned code,or any of the answers here. but commons lang is just too big if it's a small project
I created a recursive method that do the same thing you want.. feel free to use this...
public String repeat(String str, int count) {
return count > 0 ? repeat(str, count -1) + str: "";
}
i have the same answer on Can I multiply strings in java to repeat sequences?
public static String rep(int a,String k)
{
if(a<=0)
return "";
else
{a--;
return k+rep(a,k);
}
You can use this recursive method for you desired goal.

Finding strings' "permutation" based on alphabetical order

I would like to ask for advice, if there is a more efficient way to find a strings' permutation based on its alphabetical order, like my code below.
I'm working with strings long up to 16 characters, and huge amount of data, and running my program takes too much time and memory.
Basic representation of the problem
input: alphabet
output: 16752348
So in the word "alphabet" letter 'a' is the first in the alphabet, mark it index 1, then comes another 'a' in the fifth position, mark it 2, then comes 'b' in the sixth position, mark it 3 and so on..
In the code I don't use numbers as indexes, instead of, I use characters, so from value 65 of the ASCII value. (Because I use test long strings. But it doesn't change the main purpose). So the output of my program will be
input: alphabet
output: AFGEBCDH
public static String perm(String word){
char[] perm = new char[word.length()];
char[] wordArray = word.toCharArray();
char[] sortedWord = new char[word.length()];
sortedWord = word.toCharArray();
Arrays.sort(sortedWord);
for (int i=0; i<word.length(); i++){
for (int j=0; j<word.length(); j++){
if (sortedWord[i] == wordArray[j]){
perm[j] = (char)(65+i); //from A
wordArray[j] = '.';
j = word.length(); //in case, if the word has more of the tested char, we jump to the end of the cycle
}
}
}
return String.valueOf(perm);
}
public static void main (String [] args){
System.out.println(perm("alphabet"));
}
I looked at my previous solution, and it appears Arrays.sort() for an array of comparable takes considerably longer than on arrays of primitive types.
I tried a couple more approaches, and the following is the one that gave smaller time for large number of words:
public static String perm(String word){
int l = word.length();
int[] els = new int[l];
for (int i=0; i<l; i++) {
els[i] = (word.charAt(i) << 16) | i;
}
Arrays.sort(els);
char[] sb = new char[l];
for (int i=0; i<els.length; i++) {
sb[i] = (char)('A' + els[i] & 0xFFFF);
}
return String.valueOf(sb);
}
Note that the approach makes the implicit assumptions that the words only use the lower 15 bits of the UTF-16 encoding (true for words in english alphabet).
In terms of memory utilization, you have to be a bit careful about what you measure in Java. The fact that memory utilization may spike in one approach vs. another, is not necessarily a good indicator, as that memory may be garbage collected. All of the approaches here use temporary arrays/objects which are available to garbage collection after perm() is executed (except for the returned string). Now, if you care about reducing memory utilization in order to reduce garbage collection (and therefore improve performance), I suspect this last approach should give good results, although I haven't measured that.
WRT memory utilization, the program you pasted won't use much. What are you doing with the returned strings in the real code?
As far as performance goes, this should be a bit better:
import java.util.Arrays;
class El implements Comparable<El>{
char c;
int idx;
public El(char c, int idx) {
this.c = c;
this.idx = idx;
}
public int compareTo(El other) {
return Character.compare(c, other.c);
}
}
public class Perm {
public static String perm(String word){
int l = word.length();
El[] els = new El[l];
for (int i=0; i<l; i++) {
els[i] = new El(word.charAt(i), i);
}
Arrays.sort(els);
StringBuilder sb = new StringBuilder(l);
for (int i=0; i<els.length; i++) {
sb.append((char)('A' + els[i].idx));
}
return sb.toString();
}
public static void main (String [] args){
System.out.println(perm("alphabet"));
}
}
Try this:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class alphabet {
public static List<CharIndexHolder> charlist = new ArrayList<CharIndexHolder>();
public static String perm(String word) {
char[] perm = new char[word.length()];
char[] wordArray = word.toCharArray();
char[] sortedWord = new char[word.length()];
sortedWord = word.toCharArray();
Arrays.sort(sortedWord);
for (int i=0; i<word.length(); i++){
for (int j=0; j<word.length(); j++){
if (sortedWord[i] == wordArray[j]){
perm[j] = (char)(65+i); //from A
wordArray[j] = '.';
j = word.length(); //in case, if the word has more of the tested char, we jump to the end of the cycle
}
}
}
return String.valueOf(perm);
}
public static String perm2(String word) {
charlist.clear();
for(int i = 0; i < word.length(); i++) {
charlist.add(new CharIndexHolder(word.charAt(i), i));
}
Collections.sort(charlist);
for(int i = 0; i < charlist.size(); i++) {
charlist.get(i).assignedindex = i;
}
char[] result = new char[word.length()];
for(int i = 0; i < result.length; i++) {
CharIndexHolder cur = charlist.get(i);
result[cur.index] =(char) (charlist.get(i).assignedindex + 65);
}
return new String(result);
}
public static void main (String [] args){
System.out.println(perm("alphabet"));
System.out.println(perm2("alphabet"));
}
}
Helper class:
public class CharIndexHolder implements Comparable<CharIndexHolder> {
public int index;
private char character;
public int assignedindex;
CharIndexHolder(Character character, int index) {
this.character = character;
this.index = index;
}
#Override
public int compareTo(CharIndexHolder o) {
if(this.character < o.character) {
return -1;
}
if(this.character > o.character) {
return 1;
}
if(this.index < o.index) {
return -1;
}
if(this.index > o.index) {
return 1;
}
return 0;
}
}
I can't think of a way to go faster than N*log(n). If you need more speed, try replacing the list with a long array, but only allocate the array once per batch (not once per call).

Constructor Parameter Value Not Implemented

I'm creating a very simple encoder that will shuffle the characters in a string. I've written it to split this string in half, forming two new variables. The user chooses the number of shuffles they want and that is passed as a parameter in the new class constructor -- which should then use that shuffle value throughout the class. Mine is not. The shuffleEncryption method is using the class variable, 0, instead. I know this must be something very obvious, but I am not catching it. :/
//From Main Class
System.out.println("Enter message to encrypt: ");
String message = input.next();
System.out.print("Number of shuffles: " );
int numShuffles = input.nextInt();
ShuffleCipher shuffle = new ShuffleCipher(numShuffles);
System.out.println(shuffle.encode(message));
//The shuffle class
public class ShuffleCipher implements MessageEncoder {
int shuffle;
public ShuffleCipher(int shuffle) {
shuffle = this.shuffle;
}
private String shuffleEncryption(String str) {
int middle = str.length()/2;
int loop = 1;
System.out.println("shift" + shuffle);
StringBuilder sb = new StringBuilder();
do {
String firstHalf = str.substring(0, middle);
System.out.println("first:" + firstHalf);
String secondHalf = str.substring(middle);
System.out.println("second:" + secondHalf);
for(int i = 0, j = 0; i < firstHalf.length(); i++, j++) {
sb = sb.append(secondHalf.charAt(i));
if(j < secondHalf.length()) {
sb = sb.append(firstHalf.charAt(i));
}
str = sb.toString();
}
loop++;
} while (loop <= shuffle);
return str;
}
#Override
public String encode(String plainText) {
String shuffled;
shuffled = shuffleEncryption(plainText);
return shuffled;
}
}
You are not setting the shuffle member variable in the constructor.
Change this:-
public ShuffleCipher(int shuffle) {
shuffle = this.shuffle;
}
to this:-
public ShuffleCipher(int shuffle) {
this.shuffle = shuffle;
}

How to set the stringtokenizer's delimiter to break text into units with a given length

I have this sequence "ggtacctcctacgggaggcagcagtgaggaattttccgcaatgggcgaaagcctgacgga" and I want to break it into 3char length units like ggt acc tcc ..etc?
Try something like:
String str[] = s.split("(?<=\\G...)");
Output
[ggt, acc, tcc, tac, ggg, agg, cag, cag, tga, gga, att, ttc, cgc, aat, ggg, cga, aag, cct, gac, gga]
You could try something like the following, where you could convert the String to a char[] and loop through them in units of 3 in order to get that String:
String str = "ggtacctcctacgggaggcagcagtgaggaattttccgcaatgggcgaaagcctgacgga";
char[] array = str.toCharArray();
List<String> result = new ArrayList<String>();
for(int i = 0; i<array.length; i+=3)
{
StringBuilder s = new StringBuilder();
for(int j = i ; j<array.length && j < i+3; j++)
{
s.append(array[j]);
}
result.add(s.toString());
}
The List results now contains strings of length three, and it does not break if the size is not a multiple of three.
Here is another solution that uses the substring method (without StringTokenizer):
public static void main(String[] args) {
String s = "ggtacctcctacgggaggcagcagtgaggaattttccgcaatgggcgaaagcctgacgga";
char[][] c = new char[s.length()/3][3];
for ( int i = 0 ; i < s.length() ; i+=3 ) {
String substring = s.substring(i, i+3);
c[i/3] = substring.toCharArray();
}
// test
for ( int i = 0 ; i < c.length ; i++ ) {
for ( int j = 0 ; j < c[0].length ; j++ ) {
System.out.print(c[i][j]);
}
System.out.println();
}
}
Do not use a Stringtokenizer. The regular expression to split is really inefficient - DNA/RNA-Strings are really long.
In Java 8 one could do following solution:
public static void main(String[] args) {
String str = "ggtacctcctacgggaggcagcagtgaggaattttccgcaatgggcgaaagcctgacgga";
List<String> collect = str.chars()
.mapToObj(accumulator(3))
.filter(s -> s != null)
.collect(Collectors.toList());
System.out.println(collect);
}
private static IntFunction<String> accumulator(final int size) {
return new CharAccumulator(size);
}
private static final class CharAccumulator implements IntFunction<String> {
private StringBuilder builder ;
private int size;
private CharAccumulator(int size) {
this.builder = new StringBuilder();
this.size = size;
}
#Override
public String apply(int value) {
builder.append((char) value);
if (builder.length() == size) {
String result = builder.toString();
builder.setLength(0);
return result;
} else {
return null;
}
}
}
It is not as easy to understand and maybe not as performant but it works also with lazy char streams (saves memory).

Unexpected type error in hangman class

I am coding a hangman class and have a little problem with an unexpected type error. This error is in the this.hidden.charAt(i) = this.original.charAt(i);. Any help will be appreciated.
public class HangmanClass {
private ArrayList<String> words = new ArrayList();
private String word;
private final static char HIDECHAR = '*';
private String original;
private String hidden;
public void HangmanWord(String original){
this.original = original;
this.hidden = this.hideWord();
}
public String getWord() {
words.add("greetings");
words.add("cheese");
words.add("making");
words.add("juvenile");
words.add("car");
word = words.get(new Random().nextInt(words.size()));
return word;
}
private String hideWord() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < word.length(); i++) {
sb.append(HIDECHAR);
}
return sb.toString();
}
public boolean checkInput(char input){
boolean found = false;
for (int i = 0; i < this.original.length(); i++) {
if (this.original.charAt(i) == input) {
found = true;
this.hidden.charAt(i) = this.original.charAt(i);
}
}
return found;
}
};
String.charAt(int) cannot be used on the left-hand side of an assignment statement. And you cannot change / mutate Strings in Java.
Use a StringBuilder.setCharAt(int index, char ch) instead.
The charAt(int) in String cannot be used to assign values. You can use something like:
this.hidden.setCharAt(i, this.original.charAt(i));
The above works on StringBuilder, as String is an immutable class in Java. In String case you can use something like (generates a new String every time):
this.hidden = this.hidden.substring(0,i) + this.original.charAt(i) + this.hidden.substring(i);

Categories