I have a function to get a a string as a repetition of an original string. I'm wondering if I use StringBuilder append, what is the Big O of the function? Is it O(nl) : n is number of repeats and l is length of the original string ?
public String getRepetition(String originalStr, Integer n){
StringBuilder str = new StringBuilder();
for(int i = 0; i < n; i++)
str.append(originalStr);
return str.toString();
}
Comparing with the approach below, which one is better?
public String getRepetition(String originalStr, Integer n){
String str = "";
for(int i = 0; i < n; i++)
str += originalStr;
return originalStr;
}
I'm not sure why other three answers are all saying both pieces of code are O(n). Assuming originalStr is not "", the first is O(n) the other O(n^2)! (That's an exclamation, not a factorial.) They teach this on the first day of Java school. C programmers get "don't use strlen in the condition of that for loop"; Java programmers get this.
String str = "";
for(int i = 0; i < n; i++)
str += originalStr;
Each time around this loop str is getting longer. It's i * orginalStr.length(). Creating a new String (assuming no wild compiler optimisations) which takes time roughly proportional to i each time.
Edit: Usually we ignore the length of the original string. But yeah, of course it's going to be proprotional, so O(nstrlen(originalStr)) and O(nn*strlen(originalStr)). By convention this is dealt with separately.
If we rewrite the code without the String abstraction, perhaps it will be clearer.
public static char[] getRepetition(char[] originalStr, int n) {
char[] str = {};
for (int i = 0; i < n; ++i) {
assert str.length == i * originalStr.length;
char[] newStr = new char[str.length + originalStr.length];
for (int j=0; j<str.length; ++j) {
newStr[j] = str[j];
}
for (int j=0; j<originalStr.length; ++j) {
newStr[str.length+j] = originalStr[j];
}
str = newStr;
}
return str;
}
(As ever, I've not bothered to so much as compile the code. Not safe to use in a nuclear reactor.)
Just for giggles, let's deabstract the first implementation.
public static char[] getRepetition(char[] originalStr, int n) {
char[] str = new char[16];
int strLen = 0;
for (int i = 0; i < n; ++i) {
assert strLen == i * originalStr.length;
// ensureCapacity
if (str.length < strLen + originalStr.length) {
// The size at least doubles,
// so this happens increasing less often.
// It wont happen again for approximately
// the same number of iterations
// as have already happened!
char[] newStr = new char[Math.min(
str.length + originalStr.length, // first time safe
str.length*2 + 2 // *2 !
)];
for (int j=0; j<strLen; ++j) {
newStr[j] = str[j];
}
str = newStr;
}
// actual append
for (int j=0; j<originalStr.length; ++j) {
str[strLen++] = originalStr[j];
}
}
// toString
char[] newStr = new char[strLen];
for (int i=0; j<newStr.length; ++i) {
newStr[i] = str[j];
}
return newStr;
}
Both of your approaches are O(n) while the first approach eliminates several temporary String(s). It isn't clear why you have made n an Integer, nor why you have not made this a static method (it depends on no instance state). Additionally, in Java 8+, you could implement it with a lambda like
public static String getRepetition(String originalStr, int n) {
return Stream.generate(() -> originalStr).limit(n).collect(Collectors.joining());
}
Also, if you're going to use a StringBuilder as in your first example, you can explicitly size it to avoid having to amortize the cost of resizing the StringBuilder
StringBuilder str = new StringBuilder(originalStr.length() * n);
In both the cases the complexity is O(n) because you are iterating n times.
The only difference in second approach is you are creating new String in each iteration i.e. at str += originalStr;
Related
I've been doing a lot of research around this topic and can't quite crack this one easily. There are a lot of valuable solutions I've come across online for solving this problem based on characters, but how would you solve this problem based on whole-word phrases to avoid the result returning a phrase that contains a partial word at the start or end of the phrase?
For example, given an Array of Strings, the output would be the most common whole-word phrase that is contained in most (not all) of the Strings within the Array.
This example below is the closest I've found so far but it only works about half of the time and includes partial word results which isn't quite what I'm after. I'm sure someone has solved this one before.
// function to find the stem (longest common
// substring) from the string array
public static String findstem(String arr[])
{
// Determine size of the array
int n = arr.length;
// Take first word from array as reference
String s = arr[0];
int len = s.length();
String res = "";
for (int i = 0; i < len; i++) {
for (int j = i + 1; j <= len; j++) {
// generating all possible substrings
// of our reference string arr[0] i.e s
String stem = s.substring(i, j);
int k = 1;
for (k = 1; k < n; k++)
// Check if the generated stem is
// common to all words
if (!arr[k].contains(stem))
break;
// If current substring is present in
// all strings and its length is greater
// than current result
if (k == n && res.length() < stem.length())
res = stem;
}
}
return res;
}
// Driver Code
public static void main(String args[])
{
String arr[] = { "grace", "graceful", "disgraceful",
"gracefully" };
String stems = findstem(arr);
System.out.println(stems);
}
Does this do what you intended. It simply checks to see if any word is a substring of itself and others.
If you want to check for real word substrings you would need to reference some dictionary which would be very time consuming.
String arr[] = { "grace", "graceful", "disgraceful",
"gracefully" };
String save = "";
int count = 0;
for (int i = 0; i < arr.length && count != arr.length; i++) {
count = 0;
for (int k = 0; k < arr.length; k++) {
if (arr[k].contains(arr[i])) {
count++;
save = arr[i];
}
}
}
System.out.println(save);
This question already has answers here:
How to replace multiple consecutive occurrences of a character with a maximum allowed number of occurences?
(2 answers)
Closed 3 years ago.
I need to write a method that takes a String as a parameter and returns a new String obtained by replacing every instance of repeated adjacent letters with a 'n' instances of that string.
For example, if "aaabcccd" as an input String and n =2, it returns "aabccd". I already tried the following code, but not getting expected output
String in = "aaadbbb";
char[] s = in.toCharArray();
int len = s.length;
int n = 2;
StringBuffer new_s = new StringBuffer("");
int count = 1;
char prev='\0';
for (int i = 0; i < len - 1; i++) {
if (s[i] == s[i + 1]) {
if(count <= n){
new_s.append(s[i]);
count++;
}else{
count=1;
}
} else {
new_s.append(s[i]);
}
}
System.out.println(new_s);
output-aaadb
expected-aadbb
Can be done with regexp magic using backreferences.
String in = "aaaaddbbbbc";
int n = 2;
String pattern = String.format("(([a-z])\\2{%d})\\2+", n - 1);
System.out.println(in.replaceAll(pattern, "$1"));
Outputs:
aaddbbc
Explanation:
The number inside {} is n-1.
([a-z]) is a capture group, matching any single lowercase letter from a to z. Since it's a second group of parentheses in the expression, it can be referenced as 2.
(([a-z])\\2{n}) means "match n+1 repetitions of same letter". It makes up a first capture group, and we'll use that as replacement
\\2+ matches all the extra repetitions of the same letter. They are discarded after replacement.
public static String test(String input, int repetitions) {
String flag = "";
String replacement = "";
String output = input;
ArrayList<Character> prevLetters = new ArrayList<Character>();
for(int x = 0; x < input.length(); x++) {
if(!prevLetters.contains(input.charAt(x))) {
for(int y = 0; y <= repetitions ; y++) {
flag += String.valueOf(input.charAt(x));
}
if(input.contains(flag)) {
replacement = flag.substring(0, flag.length()-1);
while(output.contains(flag)){
output = output.replace(flag, replacement);
}
}
flag = "";
prevLetters.add(input.charAt(x));
}
}
return output;
}
That is my solution, which follows a similar idea as yours. Rather than comparing each character value however, I thought it would be easier to simply check for a break in the rules (character appearing n+1 times in a row) and 'fix' it.
If you are interested in using your method, one potential issue that I noticed is that you aren't assigning count to 1 in your last else. You also won't have the chance to add the final character due to you only adding the character at index 'i' when the duration for the loop is len - 1.
To add one more alternative:
String in = "aaadbbbjjkllllllopp";
int n = 2;
StringBuilder sb = new StringBuilder();
char temp = in.charAt(0);
for(int i = 0; i < in.length()-1;){ // note that the incrementation of i is moved to the while loop
temp = in.charAt(i); // save current char in temp variable
int count = 0;
while (i < in.length() && in.charAt(i) == temp) { ///iterate as long as you find same chars or hit the end of the string
i++;
count++;
}
if (count > n){ // if and only if count is greater than max allowed set it to max allowed
count = n;
}
for(int j = 0; j < count; j++){ // append count chars
sb.append(temp);
}
}
System.out.println(sb.toString());
Look at this solution. You should take care of the last char in your input string, as you iterate only to the last but one.
private void replaceConsecutiveDuplicates() {
String input = "aaadbbb";
int n = 2;
StringBuffer sb = new StringBuffer();
int count = 1;
char current;
for( int i = 0; i < input.length(); ++i){
current = input.charAt(i);
if (i + 1 < input.length() && current == input.charAt(i + 1)) {
++count;
} else if (count > 1) {
for(int j = 0; j < n; ++j) {
sb.append(current);
}
count = 1;
}
else {
sb.append(current);
}
}
System.out.println(sb.toString());
}
I think you're on the right track. I'm not sure whether this is an assignment, so I don't want to just straight up give you an answer, but here are some hints that might help:
You're already iterating over the string. This is great! However, I think you want to compare the current character with the previous character, and not the next character.
You don't need to convert your input to a char array to iterate over it, just use charAt(idx)
You never seem to use prev, but I think you had the right idea in mind when you declared it!
Break your problem into two parts: When to update count and when to append a character. You can tackle both in your for loop, but instead of trying to do both things in the same if statements, break it up into multiple ifs.
The 3 things to do are:
Update Prev Value
Update Count
Update new String
Getting the right order for these and the exact implementation I'll leave to you (again, because I'm not sure if this is an assignment or not)
Update: Since others posted, here is my solution (with single for loop):
private String replaceConsecutiveDuplicates(String input, int n) {
if (input == null || input.length() < n) return input;
if (n == 0) return "";
StringBuffer sb = new StringBuffer();
int count = 1;
char prev = input.charAt(0);
sb.append(prev);
char current;
for( int i = 1; i < input.length(); i++) {
current = input.charAt(i);
if (prev == current) {
if (++count > n) continue;
} else {
count = 1;
}
prev = current;
sb.append(current);
}
return sb.toString();
}
This is my code, but I know this is not right. I have written a lot of code for such a simple task.
Sample input is:
welcome
Sample output is:
com
elc
lco
ome
wel
It should print:
your first string is 'com'
and
your last string is 'wel'
Code:
import java.io.*;
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
int k = sc.nextInt();
int k1 = k;
int j = 0;
int t = str.length();
String [] s = new String [1000];
for (int i = t, a = 0; i >= k; i--, a++) {
s[a] = str.substring(j, k1);
j++;
k1++;
}
String[] s1 = new String[j];
for (int i = 0 ; i < j; i++) {
s1[i] = s[i];
}
for (int y = 0; y < j; y++) {
for (int z = y + 1; z < j; z++) {
if(s1[z].compareTo(s1[y]) < 0) {
String temp = s1[z];
s1[z] = s1[y];
s1[y] = temp;
}
}
}
System.out.println(s1[0]);
System.out.println(s1[1]);
}
}
Note: I split my strings, but I'm not able to arrange strings in alphabetical order, and feel that I have used a lot of arrays. Is there a better way to do this?
You can
reduce the number of variables,
use collections (list in this case) instead of Arrays to avoid having to set a size (1000)
Sort using the framework
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
int k = sc.nextInt();
List<String> cutStrings = new ArrayList<String>();
for (int i = 0; i < str.length() - k; i++) {
cutStrings.add(str.substring(i, i + k));
}
Collections.sort(cutStrings);
System.out.println(cutStrings.get(0));
System.out.println(cutStrings.get(cutStrings.size()-1));
}
You can easily sort your String[] array by simply using
Arrays.sort(s);
This will sort your strings in the default order. If you need any other kind of order you can pass the comparator as a second parameter.
You can get first and last by getting s[0] and s[s.length-1]
I did a quick implementation of your requirements. It might not be exactly what you're looking for but it should get you started. :)
So, I used an ArrayList to grab the substrings and the use the Collections library to do the sorting for me. This is just one of the many ways of solving the problem, btw. The input word can vary in size so I felt that a list would be appropriate for this situation.
String s = "welcome";
List<String> words = new ArrayList<String>();
for (int i = 0; i < s.length() - 2; i++) {
String chunk = s.charAt(i) + "" + s.charAt(i + 1) + ""
+ s.charAt(i + 2);
words.add(chunk);
System.out.println(chunk);
}
Collections.sort(words);
System.out.println(words.toString());
Feel free to let me know if you have any questions or if I have made a mistake in the code.
Good luck!
Actual problem of your code is splitting. Sorting will work. If j value 1 and k1 value 3 then wel substring is coming. Next loop, (after incrementation of both j and k1 by 1) j value 2 and k1 value 4 then elc substring is coming, etc.
So, instead of
String [] s = new String [1000];
for (int i = t, a = 0; i >= k; i--, a++) {
s[a] = str.substring(j, k1);
j++;
k1++;
}
use
int k = sc.nextInt();
String [] s = new String [(str.length()/3)+1] ;
for ( int i = 0,a = 0; i<(str.length()-k); i+=k,a++)
{
s[a] = str.substring(i,(i+k));
System.out.println(s[a]);
}
s[s.length-1]=str.substring((str.length()-k),str.length());//to add remaining values
Arrays.sort(s);//sorting alphabatically
for(int i = 0; i < s.length; i++)
System.out.println(s[i]);
}
i value will be incremented by 3. In the for loop (i+=k) where k=3.
Output:
amp
com
e s
ple
wel
As in a stutter the number of times specified by the provided multiplier if the text was "dean" and the multiplier 3, the result would be "dddeeeaaannn".
public static void repeatLetters()
{
String text = "dean";
int n = 3;
StringBuilder repeat = new StringBuilder(text);
for (int i = 0; i < n; i++)
{
repeat.append("dean");
}
System.out.println(text);
}
Not getting the required result. What am I doing wrong?
Two problems:
You are not printing the String that you have manipulated, you are printing the original String you started with, ie "dean". To print the string from the StringBuilder you can use
System.out.println(repeat);
You are adding the whole word "dean" to your original word instead of adding individual letters/chars. You need to iterate through every letter in your original word and add those letters to an empty StringBuilder. Here is the basic logic you should use to get you going:
Get original word ("dean")
Create an empty StringBuilder
Parse through each letter of your original word ("dean") either by using a for loop and getting each char in the string or using String.split and parsing the Array.
For each letter in your original word, append that letter with that letter n times to your StringBuilder.
Print the string from StringBuilder once you have parsed through all the letters in the original word.
You are simply appending the word "dean" to the end of your string three times.
Try instead looping through each char in the word dean and appending that char three times.
Something like this (this is pseudocode):
StringBuilder repeat = new StringBuilder();
ForEach( letter in "dean" )
For( int i = 0 ; i < 3 ; i++ )
repeat.add(letter);
Return repeat
You are just appending the string itself n times for each character in it. You need to iterate through the string and append each character n times.
public static void repeatLetters()
{
String text = "dean";
int n = 3;
StringBuilder repeat = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
for (int j = 0; j < n; j++) {
repeat.append(text.charAt(i));
}
}
System.out.println(repeat);
}
Also, another solution would be to use regular expressions.
public static void repeatLetters()
{
String text = "dean", replace = "";
int n = 3;
for (int i = 0; i < n; i++) replace += "$1";
System.out.println(text.replaceAll("(.)", replace));
}
You are repeating the whole word here .. a possible fix is
public static void repeatLetters()
{
String text = "dean";
int n = 3;
StringBuilder repeat = new StringBuilder();
for (int i = 0; i < text.length() ; i++)
{
for (int j= 0; j< n; j++)
repeat.append(text.charAt(i));
}
System.out.println(repeat);
}
Suppose I have a string array as follows:
String[] str = {"2","4","5"};
Now I want to subtract 1 from each of its elements, ie, I want the string to be like this now:
str = {"1","3","4"};
How do I do it? Is there any way other than converting it into an integer array?
Try this,
String str[]= {"2","4","5"};
for(int i=0;i<str.length;i++)
{
str[i]=String.valueOf(Integer.parseInt(str[i])-1));
}
You have to convert them into integers, but not necessarily store into an array of integers. You can do the math in-place instead:
for(int i = 0; i < str.length; i++) {
str[i] = Integer.toString(Integer.parseInt(str[i]) - 1);
}
However, this is a code smell to me. Strings do not tend to be the best choice when doing math in general. You might also want to work with an int[] internally and convert them to strings when needed.
You would need to convert them to Integers.
If you could make some crazy constraints, you could get it a little better, for example...
Only having single digit integers in an array of characters, strictly greater than zero. You could then do the "math" by subtracting 1 from their ASCII value, but this is a pretty crazy situation to even ever have.
convert string to int : int foo = Integer.parseInt("1234");
Subtract 1 from it
Convert back to string Integer.toString(i)
That makes
for (int i = 0; i < strn.length; i++)
strn[i] := String.valueOf(Integer.parseInt(strn[i]) - 1);
}
for (int i = 0; i < str.length; i++)
str[i] := String.valueOf(Integer.parseInt(str[i]) - 1);
}
Hope this will helps you.
public String[] stringCal(String[] ele,int numbr){
String[] sCalulated = new String[ele.length];
for(int i = 0; i < ele.length ; i ++){
sCalulated[i] = String.valueOf(Integer.parseInt(ele[i])-numbr);
}
return sCalulated;
}
public static void main(String[] args) throws IOException {
String str[] = subtractOn(new String[]{"2","4","5"});
for(int k=0;k<str.length;k++){
System.out.println("Integer is :" +str[k]);
}
}
public static String[] subtractOn(String str[]){
int intArray[] = new int[str.length];
String stres[] = new String[str.length];
for(int i=0;i<str.length;i++){
intArray[i] = Integer.parseInt(str[i]);
}
for(int j=0;j<intArray.length;j++){
stres[j] = String.valueOf(intArray[j]-1);
}
return stres;
}
You can use org.apache.commons.lang3.math library to solve it in an elegant way.
for(int i = 0; i < str.length; i++) {
str[i] = NumberUtils.toInt(str[i]) - 1;
}