To do my job, I need a code that takes a word from the user, then recognizes the number of consecutive letters and outputs it in such a way that it prints the letter and the number of times it is repeated.
Example 1 input:
hhhttrew
Example 1 output:
h3t2rew
Example 2 input:
uuuuuuhhhaaajqqq
Example 2 output:
u6h3a3jq3
String text = sc.nextLine();
int len = text.length();
int repeat = 0;
char[] chars = new char[len];
// To convert string to char
for (int h = 0; h < len; h++)
{
chars[h] = text.charAt(h);
}
String finaly = "";
for (char ignored : chars)
{
for (int j = 0 ; j <len ; j++ )
{
if (chars[j] == chars[j+1])
{
finaly = String.valueOf(chars[j]);
repeat++;
finaly = String.valueOf(repeat);
}
else
{
j++;
}
}
}
System.out.println(finaly);
Here is one way. You only need a single loop. The inner loop does the work. The outer loop simply supplies test cases.
String[] data = { "uuuuuuhhhaaajqqq",
"hhhttrew","abbcccddddeeeeeffffffggggggg" };
for (String s : data) {
String result = "" + s.charAt(0);
int count = 1;
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i - 1) != s.charAt(i)) {
result += count <= 1 ? "" : count;
result += s.charAt(i);
count = 0;
}
count++;
if (i == s.length() - 1) {
result += count <= 1 ? "" : count;
}
}
System.out.printf("%-15s <-- %s%n", result, s);
}
prints
u6h3a3jq3 <-- uuuuuuhhhaaajqqq
h3t2rew <-- hhhttrew
ab2c3d4e5f6g7 <-- abbcccddddeeeeeffffffggggggg
In a comment (now deleted) you had enquired how to reverse the process. This is one way to do it.
StringBuilder
to hold the result.count
and currentChar
currentChar
currentChar
to the bufferString[] encoded =
{ "u6h3a3jq3", "h3t2rew", "ab2c3d4e5f6g7" };
for (String s : encoded) {
StringBuilder sb = new StringBuilder();
int count = 0;
char currentChar = '\0';
for (int i = 0; i < s.length();) {
if (Character.isLetter(s.charAt(i))) {
currentChar = s.charAt(i++);
}
while (i < s.length()
&& Character.isDigit(s.charAt(i))) {
count = count * 10 + s.charAt(i++) - '0';
}
count = count == 0 ? 1 : count;
sb.append(Character.toString(currentChar)
.repeat(count));
count = 0;
}
System.out.println(s + " --> " + sb);
}
prints
u6h3a3jq3 --> uuuuuuhhhaaajqqq
h3t2rew --> hhhttrew
ab2c3d4e5f6g7 --> abbcccddddeeeeeffffffggggggg
Your solution can be greatly improved by just having one loop. Also, this does not use any complex data structures, so it is very efficient.
public static String consecutiveLetters(String text) {
if (text == null || text.length() == 0) return "";
// We start with he first letter
char currentChar = text.charAt(0);
int position = 1;
int letterCount = 1;
StringBuilder outputBuilder = new StringBuilder();
while (position < text.length()) {
// get the letter at the current position
char charAtCurrentPos = text.charAt(position);
// if it is different than what we had previously, we store the letter and the number of times it appeared. Reset the counter for the new letter
if (charAtCurrentPos != currentChar) {
outputBuilder.append(currentChar);
currentChar = charAtCurrentPos;
if (letterCount > 1) {
outputBuilder.append(letterCount);
}
letterCount = 1;
} else {
letterCount++;
}
position++;
}
// Add the last character as well
outputBuilder.append(currentChar);
if (letterCount > 1) {
outputBuilder.append(letterCount);
}
return outputBuilder.toString();
}
You could obtain matches of the regular expression
(.)\1*
This produces an array of strings of contiguous characters. For example,
"uuuuuuhhhaaajqqq" => ["uuuuuu", "hhh", "aaa", "j", "qqq"]
It is then a simple matter to convert each element to its desired form:
["uuuuuu", "hhh", "aaa", "j", "qqq"] => ["u6", "h3", "a3", "j", "q3"]
and then join the elements of the latter array (with the joining string being empty) to form the desired string:
"u6h3a3jq3"
The regular expression can be broken down as follows:
( # begin capture group 1
. # match any character
) # end capture group 1
\1* # match the contents of capture group 1 zero one or more times
As an alternative to extracting matches of the regular expression above, one could split the string on matches of the the regular expression
(?<=(.))(?!\1)
This produces the same array as before:
"uuuuuuhhhaaajqqq" => ["uuuuuu", "hhh", "aaa", "j", "qqq"]
This regular expression has the following elements.
(?<= # begin positive lookbehind
( # begin capture group 1
. # match any character
) # end capture group 1
) # end positive lookbehind
(?! # begin a negative lookahead
\1 # match the contents of capture group 1
) # end negative lookahead
Matches of the this expression are zero-width , meaning that they match locations between adjacent characters. If the string were "abbc"
, for example, it would match the locations between 'a'
and 'b'
and between 'b'
and 'c'
, splitting the string into three parts.
I would take a different approach. The following algorithm will run in O(n)
and thus be asymptotically (!!!) optimal. To make this clear as this seems to have caused some confusion. There are more efficient solutions with smaller constants.
Essentially the idea is to loop over all characters and store the number of occurrences in a Map
. If an value that is not yet in the Map is encountered, we add it to the Map giving it a count of 1. If we have already encountered the value before we get that value and increase it by one. In the end we only need to iterate through the map once in order of insertion and get the key and value.
IMPORTANT : It is crucial for this implementation to use a LinkedHashMap
as the insertion order MUST be preserved. A HashMap
of TreeMap
will give you a String which has the values in undefined order. See this thread for more details on this.
The runtime for this algorithm will be O(n) as all operations on the LinkedHashMap
take O(1)
and the rest of the time we are just looping over the n
characters.
import java.util.*;
public class Application {
public static void main(String[] args) {
var result1 = compress("hhhttrew");
var result2 = compress("uuuuuuhhhaaajqqq");
System.out.println(result1);
System.out.println(result2);
}
public static String compress(String uncompressed) {
var charArr = uncompressed.toCharArray();
// LinkedHashMap is required here to preserve the order of insertion
var occurrences = new LinkedHashMap<Character, Integer>();
// iterate over all characters and count all occurrences
for (var character : charArr) {
if (occurrences.containsKey(character)) occurrences.put(character, occurrences.get(character) + 1);
else occurrences.put(character, 1);
}
// Create the target String by iterating over the map in order of insertion and concatenate key and value. Don't add the number of occurrence when the character occurs only once
var sb = new StringBuilder();
for (var entry : occurrences.entrySet()) {
sb.append(entry.getKey());
if(entry.getValue() != 1) sb.append(entry.getValue());
}
return sb.toString();
}
}
Expected output:
h3t2rew
u6h3a3jq3
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.