A string blowup method - java

I am trying to make a method that returns a version of the original string as follows: each digit 0-9 that appears in the original string is replaced by that many occurrences of the character to the right of the digit. So the string "a3tx2z" yields "attttxzzz", and "12x" yields "2xxx". A digit not followed by a character (i.e. at the end of the string) is replaced by nothing.
I've written the code but it only works for just the first digit and remains unchanged for the next ones.
public String blowUp( String str ){
StringBuffer buffer = null;
String toAdd = null;
String toReturnString = null;
if( str.length() == 0 ){
return "no string found";
}else{
for( int count = 0; count < str.length(); count++ ){
char c = str.charAt( count );
if( count == str.length() - 1 ){
if( Character.isDigit( c ) ){
return str.substring( 0, count );
}else{
return str;
}
}else if( Character.isDigit( c ) ){
char next = str.charAt( count + 1 );
buffer = new StringBuffer();
int nooftimes = Integer.parseInt(Character.toString( c ));
for( int j = 0; j < nooftimes; j++ ){
buffer.append( next );
}
toAdd = buffer.toString();
toReturnString = str.substring( 0, count ) + toAdd + str.substring( count + 1 );
return toReturnString;
}
}
return toReturnString;
}
// return toReturnString;
}

See the comments.
public String blowUp(String str) {
StringBuffer buffer = new StringBuffer();
// String toAdd = null;
// String toReturnString = null;
if (str.length() == 0) {
return "no string found";
} else {
for (int count = 0; count < str.length(); count++) {
char c = str.charAt(count);
/*
* if (count == str.length() - 1) {
*
* if (Character.isDigit(c)) {
*
* return str.substring(0, count); } else {
*
* return str; } } else
*/
if (Character.isDigit(c) && count < str.length()-1) {
char next = str.charAt(count + 1);
if (!Character.isDigit(next)) { // append only if next
// character isn't digit
// buffer = new StringBuffer();
int nooftimes = Integer.parseInt(Character.toString(c));
for (int j = 0; j < nooftimes; j++) {
buffer.append(next);
}
} else {
buffer.append(str.charAt(count+1)); // append digit followed by another digit with next digit
}
// toAdd = buffer.toString();
// toReturnString = str.substring(0, count) + toAdd
// + str.substring(count + 1);
} else {
buffer.append(c); // simply append if not digit
}
}
return buffer.toString();
}
// return toReturnString;
}

It looks like you're making this more complicated than it has to be, and in the process getting the logic a bit confused.
In pseudocode, this problem basically boils down to the following:
for every character 'c' in the input, starting from the first:
if c is not a digit:
add c to the output
otherwise (i.e. if c is a digit), if c is not the last character in the input:
let 'x' be the number represented by c
let 'n' be the next character in the input after c
add x copies of n to the output
return the output
I'll leave translating that into Java to you*.
* EDIT: or to #TheKojuEffect!

There are no less than 5 return points in your code, this is really, really bad. It makes it impossible to accurately determine what is going on in your code.
I'm old school, so I believe in one entry point and one exit point for any method, it makes it SO much easier to figure out what's going on...
Let's start with...
if( str.length() == 0 ){
return "no string found";
This should actually be returning the original String unchanged...don't you think...
if( count == str.length() - 1 ){
if( Character.isDigit( c ) ){
return str.substring( 0, count );
}else{
return str;
}
I can't even figure out why you would need this. The last character is no more special than the first, apart from the fact that if it's a digit, you should ignore it...
char next = str.charAt( count + 1 );
buffer = new StringBuffer();
int nooftimes = Integer.parseInt(Character.toString( c ));
for( int j = 0; j < nooftimes; j++ ){
buffer.append( next );
}
toAdd = buffer.toString();
toReturnString = str.substring( 0, count ) + toAdd + str.substring( count + 1 );
return toReturnString;
Then your return here! But you've only entered this section of code once! What about the rest of the String?
You also seem to be ignoring all the other characters in the String from what I can tell...
A simpler idea would be to use the StringBuffer (or StringBuilder as I prefer) and keep appending the resulting values to it, for example...
public static String blowUp(String str) {
StringBuilder sb = new StringBuilder(128);
for (int count = 0; count < str.length(); count++) {
char c = str.charAt(count);
if (Character.isDigit(c) && count < str.length() - 1) {
char next = str.charAt(count + 1);
int nooftimes = Integer.parseInt(Character.toString(c));
for (int j = 0; j < nooftimes; j++) {
sb.append(next);
}
count++;
} else if (!Character.isDigit(c)) {
sb.append(c);
}
}
return sb.toString();
}
Example input output
If I input...
a3tx2z
12x
a3tx2z1
12x1
I get
atttxzz
2x
atttxzz
2x
as output

#MadProgrammer I modified a little bit based on your code.
public String blowup(String str) {
StringBuilder sb = new StringBuilder();
for(int i =0; i < str.length(); i++) {
char c = str.charAt(i);
if(Character.isDigit(c) && i < str.length()-1) {
char next = str.charAt(i+1);
if(!Character.isDigit(next)) {
int repTimes = Integer.parseInt(Character.toString(c));
for(int k = 0; k < repTimes; k++) {
sb.append(next);
}
}
else {
sb.append(c);
}
}
else {
sb.append(c);
}
}
return new String(sb);
}

Related

Java: Find the longest substring without any number and at least one upper case character

Came across a programming exercise and was stuck. The problem is:
You need to define a valid password for an email but the only
restrictions are:
The password must contain one uppercase character
The password should not have numeric digit
Now, given a String, find the length of the longest substring which
is a valid password. For e.g Input Str = "a0Ba" , the output should
be 2 as "Ba" is the valid substring.
I used the concept of longest substring without repeating characters which I already did before but was unable to modify it to find the solution to above problem. My code for longest substring without repeating characters is:
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
// try to extend the range [i, j]
if (!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else {
set.remove(s.charAt(i++));
}
}
return ans;
}
How about
final String input = "a0Ba";
final int answer = Arrays.stream(input.split("[0-9]+"))
.filter(s -> s.matches("(.+)?[A-Z](.+)?"))
.sorted((s1, s2) -> s2.length() - s1.length())
.findFirst()
.orElse("")
.length();
out.println(answer);
Arrays.stream(input.split("[0-9]+")) splits the original string into an array of strings. The separator is any sequence of numbers (numbers aren't allowed so they serve as separators). Then, a stream is created so I can apply functional operations and transformations.
.filter(s -> s.matches("(.+)?[A-Z](.+)?")) keeps into the stream only strings that have at least one upper-case letter.
.sorted((s1, s2) -> s2.length() - s1.length()) sorts the stream by length (desc).
.findFirst() tries to get the first string of the stream.
.orElse("") returns an empty string if no string was found.
.length(); gets the length of the string.
I suggest that you split your String to have an array of strings without digit:
yourString.split("[0-9]")
Then iterate over this array (says array a) to get the longest string that contains one Upper case character:
a[i].matches("[a-z]*[A-Z]{1}[a-z]*");
You can use a simple array. The algorithm to use would be a dynamic sliding window. Here is an example of a static sliding window: What is a Sliding Window
The algorithm should be as follows:
Keep track of 2 indexes of the array of char. These 2 indexes will be referred to as front and back here, representing the front and back of the array.
Have an int (I'll name it up here) to keep track of the number of upper case char.
Set all to 0.
Use a while loop that terminates if front > N where N is the number of char given.
If the next char is not a number, add 1 to front. Then check if that char is upper case. If so, add 1 to up.
If up is at least 1, update the maximum length if necessary.
If the next char is a number, continue checking the following char if they are also numbers. Set front to the first index where the char is not a number and back to front-1.
Output the maximum length.
You can use my solution which runs in O(n) time and finds the longest part without any digit and with a capital letter:
String testString = "skjssldfkjsakdfjlskdssfkjslakdfiop7adfaijsldifjasdjfil8klsasdfŞdijpfjapodifjpoaidjfpoaidjpfi9a";
int startIndex = 0;
int longestStartIndex = 0;
int endIndex = 0;
int index = 0;
int longestLength = Integer.MIN_VALUE;
boolean foundUpperCase = false;
while(index <= testString.length()) {
if (index == testString.length() || Character.isDigit(testString.charAt(index))) {
if (foundUpperCase && index > startIndex && index - startIndex > longestLength) {
longestLength = index - startIndex;
endIndex = index;
longestStartIndex = startIndex;
}
startIndex = index + 1;
foundUpperCase = false;
} else if (Character.isUpperCase(testString.charAt(index))) {
foundUpperCase = true;
}
index++;
}
System.out.println(testString.substring(longestStartIndex, endIndex));
You don't need regular expressions. Just use a few integers to act as index pointers into the string:
int i = 0;
int longestStart = 0;
int longestEnd = 0;
while (i < s.length()) {
// Skip past all the digits.
while (i < s.length() && Character.isDigit(s.charAt(i))) {
++i;
}
// i now points to the start of a substring
// or one past the end of the string.
int start = i;
// Keep a flag to record if there is an uppercase character.
boolean hasUppercase = false;
// Increment i until you hit another digit or the end of the string.
while (i < s.length() && !Character.isDigit(s.charAt(i))) {
hasUppercase |= Character.isUpperCase(s.charAt(i));
++i;
}
// Check if this is longer than the longest so far.
if (hasUppercase && i - start > longestEnd - longestStart) {
longestEnd = i;
longestStart = start;
}
}
String longest = s.substring(longestStart, longestEnd);
Ideone demo
Whilst more verbose than regular expressions, this has the advantage of not creating any unnecessary objects: the only object created is the longest string, right at the end.
I am using modification of Kadane algorithm to search the required password length. You may use isNumeric() and isCaps() function or include inline if statements. I have shown below with functions.
public boolean isNumeric(char x){
return (x>='0'&&x<='9');
}
public boolean isCaps(char x){
return (x>='A'&&x<='Z');
}
public int maxValidPassLen(String a)
{
int max_so_far = 0, max_ending_here = 0;
boolean cFlag = false;
int max_len = 0;
for (int i = 0; i < a.length(); i++)
{
max_ending_here = max_ending_here + 1;
if (isCaps(a.charAt(i))){
cFlag = true;
}
if (isNumeric(a.charAt(i))){
max_ending_here = 0;
cFlag = false;
}
else if (max_so_far<max_ending_here){
max_so_far = max_ending_here;
}
if(cFlag&&max_len<max_so_far){
max_len = max_so_far;
}
}
return max_len;
}
Hope this helps.
There are plenty of good answers here but thought it might be of interest to add one that uses Java 8 streams:
IntStream.range(0, s.length()).boxed()
.flatMap(b -> IntStream.range(b + 1, s.length())
.mapToObj(e -> s.substring(b, e)))
.filter(t -> t.codePoints().noneMatch(Character::isDigit))
.filter(t -> t.codePoints().filter(Character::isUpperCase).count() == 1)
.mapToInt(String::length).max();
If you wanted the string (rather than just the length), then the last line can be replaced with:
.max(Comparator.comparingInt(String::length));
Which returns an Optional<String>.
I'd use Streams and Optionals:
public static String getBestPassword(String password) throws Exception {
if (password == null) {
throw new Exception("Invalid password");
}
Optional<String> bestPassword = Stream.of(password.split("[0-9]"))
.filter(TypeErasure::containsCapital)
.sorted((o1, o2) -> o1.length() > o2.length() ? 1 : 0)
.findFirst();
if (bestPassword.isPresent()) {
return bestPassword.get();
} else {
throw new Exception("No valid password");
}
}
/**
* Returns true if word contains capital
*/
private static boolean containsCapital(String word) {
return word.chars().anyMatch(Character::isUpperCase);
}
Be sure to write some unit tests
public String pass(String str){
int length = 0;
boolean uppercase = false;
String s= "";
String d= "";
for(int i=0;i<str.length();i++){
if(Character.isUpperCase(str.charAt(i)) == true){
uppercase = true;
s = s+str.charAt(i);
}else if(Character.isDigit(str.charAt(i)) == true ){
if(uppercase == true && s.length()>length){
d = s;
s = "";
length = s.length();
uppercase = false;
}
}else if(i==str.length()-1&&Character.isDigit(str.charAt(i))==false){
s = s + str.charAt(i);
if(uppercase == true && s.length()>length){
d = s;
s = "";
length = s.length();
uppercase = false;
}
}else{
s = s+str.charAt(i);
}
}
return d;}
Here is a simple solution with Scala
def solution(str: String): Int = {
val strNoDigit = str.replaceAll("[0-9]", "-")
strAlphas = strNoDigit.split("-")
Try(strAlphas.filter(_.trim.find(_.isUpper).isDefined).maxBy(_.size))
.toOption
.map(_.length)
.getOrElse(-1)
}
Another solution using tail recursion in Scala
def solution2(str: String): Int = {
val subSt = new ListBuffer[Char]
def checker(str: String): Unit = {
if (str.nonEmpty) {
val s = str.head
if (!s.isDigit) {
subSt += s
} else {
subSt += '-'
}
checker(str.tail)
}
}
checker(str)
if (subSt.nonEmpty) {
val noDigitStr = subSt.mkString.split("-")
Try(noDigitStr.filter(s => s.nonEmpty && s.find(_.isUpper).isDefined).maxBy(_.size))
.toOption
.map(_.length)
.getOrElse(-1)
} else {
-1
}
}
This is a dynamic programming problem. You can solve this yourself using a matrix. It is easy enough. Just give it a try. Take the characters of the password as the rows and columns of the matrix. Add the diagonals if the current character appended to the last character forms a valid password. Start with the smallest valid password as the initial condition.
String[] s = testString.split("[0-9]");
int length = 0;
int index = -1;
for(int i=0; i< s.length; i++){
if(s[i].matches("[a-z]*.*[A-Z].*[a-z]*")){
if(length <= s[i].length()){
length = s[i].length();
index = i;
}
}
}
if(index >= 0){
System.out.println(s[index]);
}
//easiest way to do it:
String str = "a0Ba12hgKil8oPlk";
String[] str1 = str.split("[0-9]+");
List<Integer> in = new ArrayList<Integer>();
for (int i = 0; i < str1.length; i++) {
if (str1[i].matches("(.+)?[A-Z](.+)?")) {
in.add(str1[i].length());
} else {
System.out.println(-1);
}
}
Collections.sort(in);
System.out.println("string : " + in.get(in.size() - 1));
This is my solution with c#. I tested a range of strings and it gave me the correct value. Used Split. No Regex or Substrings. Let me know if it works; open to improvements and corrections.
public static int validPassword(string str)
{
List<int> strLength = new List<int>();
if (!(str.All(Char.IsDigit)))
{
//string str = "a0Bb";
string[] splitStrs = str.Split(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
//check if each string contains a upper case
foreach (string s in splitStrs)
{
//Console.WriteLine(s);
if (s.Any(char.IsUpper) && s.Any(char.IsLower) || s.Any(char.IsUpper))
{
strLength.Add(s.Length);
}
}
if (strLength.Count == 0)
{
return -1;
}
foreach (int i in strLength)
{
//Console.WriteLine(i);
}
return strLength.Max();
}
else
{
return -1;
}
}
I think this solution takes care of all the possible corner cases. It passed all the test cases in an Online Judge. It is a dynamic sliding window O(n) solution.
public class LongestString {
public static void main(String[] args) {
// String testString = "AabcdDefghIjKL0";
String testString = "a0bb";
int startIndex = 0, endIndex = 0;
int previousUpperCaseIndex = -1;
int maxLen = 0;
for (; endIndex < testString.length(); endIndex++) {
if (Character.isUpperCase(testString.charAt(endIndex))) {
if (previousUpperCaseIndex > -1) {
maxLen = Math.max(maxLen, endIndex - startIndex);
startIndex = previousUpperCaseIndex + 1;
}
previousUpperCaseIndex = endIndex;
} else if (Character.isDigit(testString.charAt(endIndex))) {
if (previousUpperCaseIndex > -1) {
maxLen = Math.max(maxLen, endIndex - startIndex);
}
startIndex = endIndex + 1;
previousUpperCaseIndex = -1;
}
}
if (previousUpperCaseIndex > -1)
maxLen = Math.max(maxLen, endIndex - startIndex);
System.out.println(maxLen);
}}
function ValidatePassword(password){
var doesContainNumber = false;
var hasUpperCase = false;
for(var i=0;i<password.length;i++){
if(!isNaN(password[i]))
doesContainNumber = true;
if(password[i] == password[i].toUpperCase())
hasUpperCase = true;
}
if(!doesContainNumber && hasUpperCase)
return true;
else
return false;
}
function GetLongestPassword(inputString){
var longestPassword = "";
for(var i=0;i<inputString.length-1;i++)
{
for (var j=i+1;j<inputString.length;j++)
{
var substring = inputString.substring(i,j+1);
var isValid = ValidatePassword(substring);
if(isValid){
if(substring.length > longestPassword.length)
{
longestPassword = substring;
}
}
}
}
if(longestPassword == "")
{
return "No Valid Password found";
}
else
{
return longestPassword;
}
}

Greatest number of consecutive vowels

I'm trying to write a function that takes in a String and returns the greatest number of consecutive equivalent vowels in the String.
Here's my attempt:
public static final String VOWELS = "aeiou";
public static int consecutiveVowelsInLine(String line) {
int longestVowels = 0;
int candidateLength = 0;
for (int i = 0; i < line.length() - 1; i++) {
if (isVowel(line.charAt(i))) {
if (line.charAt(i) == line.charAt(i+1)) {
candidateLength++;
}
} else {
candidateLength = 0;
}
longestVowels = Math.max(longestVowels, candidateLength);
}
return longestVowels;
}
public static boolean isVowel(char c) {
VOWELS.contains(c.toLowerCase());
}
The problem is this doesn't handle the case where the String is a single character that's a vowel. So if the String is just "a", my code gives back 0 instead of 1.
As said before, the vowels have to be the same.
Testcases:
a -> 1
b -> 0
ae -> 1
aeae -> 1
aab -> 2
aba -> 1
abee -> 2
I think you aim to do too much in the loop: instead of looking to the character next, concentrate on the current character and maintain a state that stores the previous vowel:
public static int consecutiveVowelsInLine(String line) {
int longestVowels = 0;
int candidateLength = 0;
char vowel = 'b'; //b is not a vowel
for (int i = 0; i < line.length(); i++) {
char ci = line.charAt(i);
if (isVowel(ci)) {
if (ci == vowel) { //the same as the other one
candidateLength++;
} else {
candidateLength = 1;
}
vowel = ci;
} else {
candidateLength = 0;
vowel = 'b';
}
longestVowels = Math.max(longestVowels, candidateLength);
}
return longestVowels;
}
Here vowel stores the current vowel sequences you are working with. In the beginning we use b, simple because that is not a vowe. In case we encounter a vowel, that vowel is stores in vowel and we update the candidateLength accordingly. In case we encounter a non-vowel, we set vowel back to b (or another non-vowel).
Demo:
There were some problems with your isVowel method as well, a running implementation with a few testcases can be found here.
Here's one problem:
if (line.charAt(i) == line.charAt(i+1)) {
candidateLength++;
}
If the string is only one character, you're checking the character against null. Add a check, something like this:
if (line.length() == 1 && isVowel(line.charAt(0)) {
etc.
}
Simply change it like:
public static int consecutiveVowelsInLine( String line ){
int result = findConsecutiveMaxValue( line );
if( result == 0 ){
result = findSingleVowel( line );
}
return result;
}
private static int findSingleVowel( String line ){
for( int i = 0; i < line.length(); i++ ){
if( isVowel( line.charAt( i ) ) ){ return 1; }
}
return 0;
}
private static int findConsecutiveMaxValue( String line ){
int longestVowels = 0;
int candidateLength = 0;
for( int i = 0; i < line.length() - 1; i++ ){
if( isVowel( line.charAt( i ) ) ){
if( line.charAt( i ) == line.charAt( i + 1 ) ){
candidateLength++;
}
}
else{
candidateLength = 0;
}
longestVowels = Math.max( longestVowels, candidateLength );
}
return longestVowels;
}
Change:
if (line.charAt(i) == line.charAt(i+1)) {
candidateLength++;
}
to:
if (candidateLength == 0 || line.charAt(i) == line.charAt(i-1)) {
candidateLength++;
}
Additionally the condition in for() loop looks suspicious - use getLength() instead of getLength()-1.

implement basic string compression

I am working on question 1.5 from the book Cracking The Coding interview. The problem is to take a string "aabcccccaaa" and turn it into a2b1c5a3.
If the compressed string is not smaller than the original string, then return the original string.
My code is below. I used an ArrayList because I would not know how long the compressed string would be.
My output is [a, 2, b, 1, c, 5], aabc, []. When the program gets to the end of string, it doesn't have a character to compare the last character too.
import java.util.*;
import java.io.*;
public class stringCompression {
public static void main(String[] args) {
String a = "aabcccccaaa";
String b = "aabc";
String v = "aaaa";
check(a);
System.out.println("");
check(b);
System.out.println("");
check(v);
}
public static void check(String g){
ArrayList<Character> c = new ArrayList<Character>();
int count = 1;
int i = 0;
int h = g.length();
for(int j = i + 1; j < g.length(); j++)
{
if(g.charAt(i) == g.charAt(j)){
count++;
}
else {
c.add(g.charAt(i));
c.add((char)( '0' + count));
i = j;
count = 1;
}
}
if(c.size() == g.length()){
System.out.print(g);
}
else{
System.out.print(c);
}
}
}
In the last loop you're not adding the result to the array. When j = g.length() still needs to add the current char and count to the array. So you could check the next value of j before increment it:
for(int j = i + 1; j < g.length(); j++)
{
if(g.charAt(i) == g.charAt(j)){
count++;
}
else {
c.add(g.charAt(i));
c.add((char)( '0' + count));
i = j;
count = 1;
}
if((j + 1) = g.length()){
c.add(g.charAt(i));
c.add((char)( '0' + count));
}
}
I would use a StringBuilder rather than an ArrayList to build your compressed String. When you start compressing, the first character should already be added to the result. The count of the character will be added once you've encountered a different character. When you've reached the end of the String you should just be appending the remaining count to the result for the last letter.
public static void main(String[] args) throws Exception {
String[] data = new String[] {
"aabcccccaaa",
"aabc",
"aaaa"
};
for (String d : data) {
System.out.println(compress(d));
}
}
public static String compress(String str) {
StringBuilder compressed = new StringBuilder();
// Add first character to compressed result
char currentChar = str.charAt(0);
compressed.append(currentChar);
// Always have a count of 1
int count = 1;
for (int i = 1; i < str.length(); i++) {
char nextChar = str.charAt(i);
if (currentChar == nextChar) {
count++;
} else {
// Append the count of the current character
compressed.append(count);
// Set the current character and count
currentChar = nextChar;
count = 1;
// Append the new current character
compressed.append(currentChar);
}
}
// Append the count of the last character
compressed.append(count);
// If the compressed string is not smaller than the original string, then return the original string
return (compressed.length() < str.length() ? compressed.toString() : str);
}
Results:
a2b1c5a3
aabc
a4
You have two errors:
one that Typo just mentioned, because your last character was not added;
and another one, if the original string is shorter like "abc" with only three chars: "a1b1c1" has six chars (the task is "If the compressed string is not smaller than the original string, then return the original string.")
You have to change your if statement, ask for >= instead of ==
if(c.size() >= g.length()){
System.out.print(g);
} else {
System.out.print(c);
}
Use StringBuilder and then iterate on the input string.
private static string CompressString(string inputString)
{
var count = 1;
var compressedSb = new StringBuilder();
for (var i = 0; i < inputString.Length; i++)
{
// Check if we are at the end
if(i == inputString.Length - 1)
{
compressedSb.Append(inputString[i] + count.ToString());
break;
}
if (inputString[i] == inputString[i + 1])
count++;
else
{
compressedSb.Append(inputString[i] + count.ToString());
count = 1;
}
}
var compressedString = compressedSb.ToString();
return compressedString.Length > inputString.Length ? inputString : compressedString;
}

Array size issue for converting String to Unicode

I have a string "text" in one class which calls on a method in another class to convert text in various ways. In this method though I am left with an "ArrayIndexOutOfBoundsException" error.
public String toUnicode() {
char unicodeTextArray[] = new char[text.length()];
if (text == null || text.isEmpty()) {
return "";
}
String unicodeTextArrayString[] = new String[text.length()];
for (int i = 0; i < text.length(); i++) {
unicodeTextArray[i] = text.charAt(i);
if (unicodeTextArray[i] < 0x10) {
unicodeTextArrayString[i] = "\\u000" + Integer.toHexString(unicodeTextArray[i]);
} else if (unicodeTextArray[i] < 0x100) {
unicodeTextArrayString[i] = "\\u00" + Integer.toHexString(unicodeTextArray[i]);
} else if (unicodeTextArray[i] < 0x1000) {
unicodeTextArrayString[i] = "\\u0" + Integer.toHexString(unicodeTextArray[i]);
}
unicodeTextArrayString[i] = "\\u" + Integer.toHexString(unicodeTextArray[i]);
}
String unicode = unicodeTextArrayString[text.length()];
return unicode;
}
Changing one line to an arbitrarily large number such as:
String unicodeTextArrayString[] = new String[9999];
Results in no error, but it returns null.
I thought about setting an int variable to increase the length of the array, but * 4 was still too small of an array size and it seems like if I go too large it just returns null.
How could I go about getting the correct length of the array?
EDIT: I found a non-array approach that works, but I would still like to know if there is a way to make the above array approach work in some way.
public String toUnicode()
{
String unicodeString = "";
for (int i = 0; i < text.length(); i++)
{
char c = text.charAt(i);
String s = String.format ("\\u%04x", (int)c);
unicodeString = unicodeString + s;
}
return unicodeString;
}
EDIT 2: In case anyone reading this is curious, to get the decimal value of the unicode:
public String toUnicode()
{
String unicodeString = "";
for (int i = 0; i < text.length(); i++)
{
char c = text.charAt(i);
int unicodeDecimal = c;
unicodeString = unicodeString + unicodeDecimal + " ";
}
return unicodeString;
}
EDIT 3: I ended up deciding to use the following, which separates unicode decimals by space, and checks for the unicode value 10 (which means new line) and outputs a new line into the string instead of that value.
public String toUnicode()
{
String unicodeString = "";
for (int i = 0; i < text.length(); i++)
{
char c = text.charAt(i);
int unicodeDecimal = c;
if (unicodeDecimal == 10)
{
unicodeString = unicodeString + "\n";
}
else
{
unicodeString = unicodeString + unicodeDecimal + " ";
}
}
return unicodeString;
}
couple of things
1) Move line
char unicodeTextArray[] = new char[text.length()]; after following code
if (text == null || text.isEmpty())
{
return "";
}
char unicodeTextArray[] = new char[text.length()];
2) Error is because of this String unicode = unicodeTextArrayString[text.length()];
e.g you get a text as "hello", then you initialized unicodeTextArrayString of size text.length() which is 5. So you can fetch back from this array for index 0 to 4 only, but you are trying to fetch from index 5, which is out of bounds.
3) After having said so, the code/logic seems wrong. I just modified your logic using StringBuilder instead. You can check for conversion logic
public static String toUnicode(String text)
{
if (text == null || text.isEmpty())
{
return "";
}
StringBuilder unicodeTextArrayString = new StringBuilder();
for (int i = 0; i < text.length(); i++)
{
char ch = text.charAt(i);
if (ch < 0x10)
{
unicodeTextArrayString.append("\\u000" + Integer.toHexString(ch));
}
else if (ch < 0x100)
{
unicodeTextArrayString.append("\\u00" + Integer.toHexString(ch));
}
else if (ch < 0x1000)
{
unicodeTextArrayString.append("\\u0" + Integer.toHexString(ch));
}
else
{
unicodeTextArrayString.append("\\u" + Integer.toHexString(ch));
}
}
return unicodeTextArrayString.toString();
}
4) If you want to use array based approach, then add each chars to arrays, and then iterate again through array where u stored chars, and then build a string (instead of getting a string from last index) and then return the string
this one is the culprit
String unicode = unicodeTextArrayString[text.length()];
edit:
If you really want to make the original code to work in some way, I think there are several ways to do it. The following code is one of them.
public String toUnicode() {
char unicodeTextArray[] = new char[text.length()];
if (text == null) {
return "";
}
String unicodeTextArrayString[] = new String[text.length()];
StringBuilder unicode= new StringBuilder();
for (int i = 0; i < text.length(); i++) {
unicodeTextArray[i] = text.charAt(i);
if (unicodeTextArray[i] < 0x10) {
unicodeTextArrayString[i] = "\\u000" + Integer.toHexString(unicodeTextArray[i]);
} else if (unicodeTextArray[i] < 0x100) {
unicodeTextArrayString[i] = "\\u00" + Integer.toHexString(unicodeTextArray[i]);
} else if (unicodeTextArray[i] < 0x1000) {
unicodeTextArrayString[i] = "\\u0" + Integer.toHexString(unicodeTextArray[i]);
} else
unicodeTextArrayString[i] = "\\u" + Integer.toHexString(unicodeTextArray[i]);
unicode = unicode.append(unicodeTextArrayString[i]);
}
return unicode.toString();
}

Compression algorithm in java

My goal is to write a program that compresses a string, for example:
input: hellooopppppp!
output:he2l3o6p!
Here is the code I have so far, but there are errors.
When I have the input: hellooo
my code outputs: hel2l3o
instead of: he213o
the 2 is being printed in the wrong spot, but I cannot figure out how to fix this.
Also, with an input of: hello
my code outputs: hel2l
instead of: he2lo
It skips the last letter in this case all together, and the 2 is also in the wrong place, an error from my first example.
Any help is much appreciated. Thanks so much!
public class compressionTime
{
public static void main(String [] args)
{
System.out.println ("Enter a string");
//read in user input
String userString = IO.readString();
//store length of string
int length = userString.length();
System.out.println(length);
int count;
String result = "";
for (int i=1; i<=length; i++)
{
char a = userString.charAt(i-1);
count = 1;
if (i-2 >= 0)
{
while (i<=length && userString.charAt(i-1) == userString.charAt(i-2))
{
count++;
i++;
}
System.out.print(count);
}
if (count==1)
result = result.concat(Character.toString(a));
else
result = result.concat(Integer.toString(count).concat(Character.toString(a)));
}
IO.outputStringAnswer(result);
}
}
I would
count from 0 as that is how indexes work in Java. Your code will be simpler.
would compare the current char to the next one. This will avoid printing the first character.
wouldn't compress ll as 2l as it is no smaller. Only sequences of at least 3 will help.
try to detect if a number 3 to 9 has been used and at least print an error.
use the debugger to step through the code to understand what it is doing and why it doesn't do what you think it should.
I am doing it this way. Very simple:
public static void compressString (String string) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < string.length(); i++) {
int count = 1;
while (i + 1 < string.length()
&& string.charAt(i) == string.charAt(i + 1)) {
count++;
i++;
}
if (count > 1) {
stringBuffer.append(count);
}
stringBuffer.append(string.charAt(i));
}
System.out.println("Compressed string: " + stringBuffer);
}
You can accomplish this using a nested for loops and do something simial to:
count = 0;
String results = "";
for(int i=0;i<userString.length();){
char begin = userString.charAt(i);
//System.out.println("begin is: "+begin);
for(int j=i+1; j<userString.length();j++){
char next = userString.charAt(j);
//System.out.println("next is: "+next);
if(begin == next){
count++;
}
else{
System.out.println("Breaking");
break;
}
}
i+= count+1;
if(count>0){
String add = begin + "";
int tempcount = count +1;
results+= tempcount + add;
}
else{
results+= begin;
}
count=0;
}
System.out.println(results);
I tested this output with Hello and the result was He2lo
also tested with hellooopppppp result he2l3o6p
If you don't understand how this works, you should learn regular expressions.
public String rleEncodeString(String in) {
StringBuilder out = new StringBuilder();
Pattern p = Pattern.compile("((\\w)\\2*)");
Matcher m = p.matcher(in);
while(m.find()) {
if(m.group(1).length() > 1) {
out.append(m.group(1).length());
}
out.append(m.group(2));
}
return out.toString();
}
Try something like this:
public static void main(String[] args) {
System.out.println("Enter a string:");
Scanner IO = new Scanner(System.in);
// read in user input
String userString = IO.nextLine() + "-";
int length = userString.length();
int count = 0;
String result = "";
char new_char;
for (int i = 0; i < length; i++) {
new_char = userString.charAt(i);
count++;
if (new_char != userString.charAt(i + 1)) {
if (count != 1) {
result = result.concat(Integer.toString(count + 1));
}
result = result.concat(Character.toString(new_char));
count = 0;
}
if (userString.charAt(i + 1) == '-')
break;
}
System.out.println(result);
}
The problem is that your code checks if the previous letter, not the next, is the same as the current.
Your for loops basically goes through each letter in the string, and if it is the same as the previous letter, it figures out how many of that letter there is and puts that number into the result string. However, for a word like "hello", it will check 'e' and 'l' (and notice that they are preceded by 'h' and 'e', receptively) and think that there is no repeat. It will then get to the next 'l', and then see that it is the same as the previous letter. It will put '2' in the result, but too late, resulting in "hel2l" instead of "he2lo".
To clean up and fix your code, I recommend the following to replace your for loop:
int count = 1;
String result = "";
for(int i=0;i<length;i++) {
if(i < userString.length()-1 && userString.charAt(i) == userString.charAt(i+1))
count++;
else {
if(count == 1)
result += userString.charAt(i);
else {
result = result + count + userString.charAt(i);
count = 1;
}
}
}
Comment if you need me to explain some of the changes. Some are necessary, others optional.
Here is the solution for the problem with better time complexity:
public static void compressString (String string) {
LinkedHashSet<String> charMap = new LinkedHashSet<String>();
HashMap<String, Integer> countMap = new HashMap<String, Integer>();
int count;
String key;
for (int i = 0; i < string.length(); i++) {
key = new String(string.charAt(i) + "");
charMap.add(key);
if(countMap.containsKey(key)) {
count = countMap.get(key);
countMap.put(key, count + 1);
}
else {
countMap.put(key, 1);
}
}
Iterator<String> iterator = charMap.iterator();
String resultStr = "";
while (iterator.hasNext()) {
key = iterator.next();
count = countMap.get(key);
if(count > 1) {
resultStr = resultStr + count + key;
}
else{
resultStr = resultStr + key;
}
}
System.out.println(resultStr);
}

Categories