So I'm supposed to make a list that contains the character that follows each non-tail occurrence of a pattern in a text. The character stored at index n of the list must be the character that followed the nth non-tail occurrence of the pattern.
Ex: The test case getCharsThatFollowPattern("abcabdabcab", "ab") should return the ArrayList ['c', 'd', 'c'].
I'm having problems trying to get the list to iterate through the pattern text. I get ['a', 'a', 'a'] in my test case instead of ['c', 'd', 'c'].
public static ArrayList<Character> getCharsThatFollowPattern (String text, String pattern) {
ArrayList<Character> character = new ArrayList<Character>();
int i = 0;
while (i < text.length())
{
if (i < text.length())
{
character.add(text.charAt(text.indexOf(pattern, i)));
i = i + text.indexOf(pattern, i) + pattern.length();
}
}
return character;
}
Why wont it iterate through? :(
Your intent is to find a substring pattern inside the larger text value, and then add each of those characters to an array. There are a few things going on in your code, so I tried to create the simplest example that shows how to get it to work, but exclude things that you were already fine with (like creating an array and populating values).
The code below finds where the next pattern starts – indexOfNextPatternStart – that would be where ever "ab" occurs next. Then it adds the length of "ab" itself to get whatever index is after that. So in your text example, it starts with "abc" – "ab" is clearly at the front, but you want the index of "c". That's what indexOfCharAfterPattern contains. At that point, you've got the character isolated. In my example, I'm storing that temporarily as charAfterPattern and printing it out.
I added the if (indexOfCharAfterPattern < text.length()) check to protect against the last "ab" in text. There's nothing after that last "ab", so trying to look ahead for the next character won't work.
public static void printThatFollowPattern(String text, String pattern) {
int i = 0;
while (i < text.length()) {
int indexOfNextPatternStart = text.indexOf(pattern, i);
int indexOfCharAfterPattern = indexOfNextPatternStart + pattern.length();
if (indexOfCharAfterPattern < text.length()) {
char charAfterPattern = text.charAt(indexOfCharAfterPattern);
System.out.println("charAfterPattern: " + charAfterPattern);
} else {
break;
}
i = indexOfCharAfterPattern + 1;
}
}
Related
Given is a String word and a String array book that contains some strings. The program should give out the number of possibilities to create word only using elements in book. An element can be used as many times as we want and the program must terminate in under 6 seconds.
For example, input:
String word = "stackoverflow";
String[] book = new String[9];
book[0] = "st";
book[1] = "ck";
book[2] = "CAG";
book[3] = "low";
book[4] = "TC";
book[5] = "rf";
book[6] = "ove";
book[7] = "a";
book[8] = "sta";
The output should be 2, since we can create "stackoverflow" in two ways:
1: "st" + "a" + "ck" + "ove" + "rf" + "low"
2: "sta" + "ck" + "ove" + "rf" + "low"
My implementation of the program only terminates in the required time if word is relatively small (<15 characters). However, as I mentioned before, the running time limit for the program is 6 seconds and it should be able to handle very large word strings (>1000 characters). Here is an example of a large input.
Here is my code:
1) the actual method:
input: a String word and a String[] book
output: the number of ways word can be written only using strings in book
public static int optimal(String word, String[] book){
int count = 0;
List<List<String>> allCombinations = allSubstrings(word);
List<String> empty = new ArrayList<>();
List<String> wordList = Arrays.asList(book);
for (int i = 0; i < allCombinations.size(); i++) {
allCombinations.get(i).retainAll(wordList);
if (!sumUp(allCombinations.get(i), word)) {
allCombinations.remove(i);
allCombinations.add(i, empty);
}
else count++;
}
return count;
}
2) allSubstrings():
input: a String input
output: A list of lists, each containing a combination of substrings that add up to input
static List<List<String>> allSubstrings(String input) {
if (input.length() == 1) return Collections.singletonList(Collections.singletonList(input));
List<List<String>> result = new ArrayList<>();
for (List<String> temp : allSubstrings(input.substring(1))) {
List<String> firstList = new ArrayList<>(temp);
firstList.set(0, input.charAt(0) + firstList.get(0));
if (input.startsWith(firstList.get(0), 0)) result.add(firstList);
List<String> l = new ArrayList<>(temp);
l.add(0, input.substring(0, 1));
if (input.startsWith(l.get(0), 0)) result.add(l);
}
return result;
}
3.) sumup():
input: A String list input and a String expected
output: true if the elements in input add up to expected
public static boolean sumUp (List<String> input, String expected) {
String x = "";
for (int i = 0; i < input.size(); i++) {
x = x + input.get(i);
}
if (expected.equals(x)) return true;
return false;
}
I've figured out what I was doing wrong in my previous answer: I wasn't using memoization, so I was redoing an awful lot of unnecessary work.
Consider a book array {"a", "aa", "aaa"}, and a target word "aaa". There are four ways to construct this target:
"a" + "a" + "a"
"aa" + "a"
"a" + "aa"
"aaa"
My previous attempt would have walk through all four, separately. But instead, one can observe that:
There is 1 way to construct "a"
You can construct "aa" in 2 ways, either "a" + "a" or using "aa" directly.
You can construct "aaa" either by using "aaa" directly (1 way); or "aa" + "a" (2 ways, since there are 2 ways to construct "aa"); or "a" + "aa" (1 way).
Note that the third step here only adds a single additional string to a previously-constructed string, for which we know the number of ways it can be constructed.
This suggests that if we count the number of ways in which a prefix of word can be constructed, we can use that to trivially calculate the number of ways a longer prefix by adding just one more string from book.
I defined a simple trie class, so you can quickly look up prefixes of the book words that match at any given position in word:
class TrieNode {
boolean word;
Map<Character, TrieNode> children = new HashMap<>();
void add(String s, int i) {
if (i == s.length()) {
word = true;
} else {
children.computeIfAbsent(s.charAt(i), k -> new TrieNode()).add(s, i + 1);
}
}
}
For each letter in s, this creates an instance of TrieNode, and stores the TrieNode for the subsequent characters etc.
static long method(String word, String[] book) {
// Construct a trie from all the words in book.
TrieNode t = new TrieNode();
for (String b : book) {
t.add(b, 0);
}
// Construct an array to memoize the number of ways to construct
// prefixes of a given length: result[i] is the number of ways to
// construct a prefix of length i.
long[] result = new long[word.length() + 1];
// There is only 1 way to construct a prefix of length zero.
result[0] = 1;
for (int m = 0; m < word.length(); ++m) {
if (result[m] == 0) {
// If there are no ways to construct a prefix of this length,
// then just skip it.
continue;
}
// Walk the trie, taking the branch which matches the character
// of word at position (n + m).
TrieNode tt = t;
for (int n = 0; tt != null && n + m <= word.length(); ++n) {
if (tt.word) {
// We have reached the end of a word: we can reach a prefix
// of length (n + m) from a prefix of length (m).
// Increment the number of ways to reach (n+m) by the number
// of ways to reach (m).
// (Increment, because there may be other ways).
result[n + m] += result[m];
if (n + m == word.length()) {
break;
}
}
tt = tt.children.get(word.charAt(n + m));
}
}
// The number of ways to reach a prefix of length (word.length())
// is now stored in the last element of the array.
return result[word.length()];
}
For the very long input given by OP, this gives output:
$ time java Ideone
2217093120
real 0m0.126s
user 0m0.146s
sys 0m0.036s
Quite a bit faster than the required 6 seconds - and this includes JVM startup time too.
Edit: in fact, the trie isn't necessary. You can simply replace the "Walk the trie" loop with:
for (String b : book) {
if (word.regionMatches(m, b, 0, b.length())) {
result[m + b.length()] += result[m];
}
}
and it performs slower, but still way faster than 6s:
2217093120
real 0m0.173s
user 0m0.226s
sys 0m0.033s
A few observations:
x = x + input.get(i);
As you are looping, using String+ isn't a good idea. Use a StringBuilder and append to that within the loop, and in the end return builder.toString(). Or you follow the idea from Andy. There is no need to merge strings, you already know the target word. See below.
Then: List implies that adding/removing elements might be costly. So see if you can get rid of that part, and if it would be possible to use maps, sets instead.
Finally: the real point would be to look into your algorithm. I would try to work "backwards". Meaning: first identify those array elements that actually occur in your target word. You can ignore all others right from start.
Then: look at all array entries that **start*+ your search word. In your example you can notice that there are just two array elements that fit. And then work your way from there.
My first observation would be that you don't actually need to build anything: you know what string you are trying to construct (e.g. stackoverflow), so all you really need to keep track of is how much of that string you have matched so far. Call this m.
Next, having matched m characters, provided m < word.length(), you need to choose a next string from book which matches the portion of word from m to m + nextString.length().
You could do this by checking each string in turn:
if (word.matches(m, nextString, 0, nextString.length()) { ...}
But you can do better, by determining strings that can't match in advance: the next string you append will have the following properties:
word.charAt(m) == nextString.charAt(0) (the next characters match)
m + nextString.length() <= word.length() (adding the next string shouldn't make the constructed string longer than word)
So, you can cut down the potential words from book that you might check by constructing a map of letters to words that start with that (point 1); and if you store the words with the same starting letter in increasing length order, you can stop checking that letter as soon as the length gets too big (point 2).
You can construct a map once and reuse:
Map<Character, List<String>> prefixMap =
Arrays.asList(book).stream()
.collect(groupingBy(
s -> s.charAt(0),
collectingAndThen(
toList(),
ss -> {
ss.sort(comparingInt(String::length));
return ss;
})));
You can count the number of ways recursively, without constructing any additional objects (*):
int method(String word, String[] book) {
return method(word, 0, /* construct map as above */);
}
int method(String word, int m, Map<Character, List<String>> prefixMap) {
if (m == word.length()) {
return 1;
}
int result = 0;
for (String nextString : prefixMap.getOrDefault(word.charAt(m), emptyList())) {
if (m + nextString.length() > word.length()) {
break;
}
// Start at m+1, because you already know they match at m.
if (word.regionMatches(m + 1, nextString, 1, nextString.length()-1)) {
// This is a potential match!
// Make a recursive call.
result += method(word, m + nextString.length(), prefixMap);
}
}
return result;
}
(*) This may construct new instances of Character, because of the boxing of the word.charAt(m): cached instances are guaranteed to be used for chars in the range 0-127 only. There are ways to work around this, but they would only clutter the code.
I think you are already doing a pretty good job at optimizing your application. In addition to the answer by GhostCat here are a few suggestions of my own:
public static int optimal(String word, String[] book){
int count = 0;
List<List<String>> allCombinations = allSubstrings(word);
List<String> wordList = Arrays.asList(book);
for (int i = 0; i < allCombinations.size(); i++)
{
/*
* allCombinations.get(i).retainAll(wordList);
*
* There is no need to retrieve the list element
* twice, just set it in a local variable
*/
java.util.List<String> combination = allCombinations.get(i);
combination.retainAll(wordList);
/*
* Since we are only interested in the count here
* there is no need to remove and add list elements
*/
if (sumUp(combination, word))
{
/*allCombinations.remove(i);
allCombinations.add(i, empty);*/
count++;
}
/*else count++;*/
}
return count;
}
public static boolean sumUp (List<String> input, String expected) {
String x = "";
for (int i = 0; i < input.size(); i++) {
x = x + input.get(i);
}
// No need for if block here, just return comparison result
/*if (expected.equals(x)) return true;
return false;*/
return expected.equals(x);
}
And since you are interested in seeing the execution time of your method I would recommend implementing a benchmarking system of some sort. Here is a quick mock-up:
private static long benchmarkOptima(int cycles, String word, String[] book) {
long totalTime = 0;
for (int i = 0; i < cycles; i++)
{
long startTime = System.currentTimeMillis();
int a = optimal(word, book);
long executionTime = System.currentTimeMillis() - startTime;
totalTime += executionTime;
}
return totalTime / cycles;
}
public static void main(String[] args)
{
String word = "stackoverflow";
String[] book = new String[] {
"st", "ck", "CAG", "low", "TC",
"rf", "ove", "a", "sta"
};
int result = optimal(word, book);
final int cycles = 50;
long averageTime = benchmarkOptima(cycles, word, book);
System.out.println("Optimal result: " + result);
System.out.println("Average execution time - " + averageTime + " ms");
}
Output
2
Average execution time - 6 ms
Note: The implementation is getting stuck in the test case mentioned by #user1221, working on it.
What I could think of is a Trie based approach that is O(sum of length of words in dict) space. Time is not optimal.
Procedure:
Build a Trie of all the words in the dictionary. This is a pre-processing task that will take O(sum of lengths of all strings in dict).
We try finding the string that you want to make in the trie, with a twist. We start with searching a prefix of the string. If we get a prefix in the trie, we start the search from the top recursively and continue to look for more prefixes.
When we reach the end of out string i.e. stackoverflow, we check if we arrived at the end of any string, if yes, then we reached a valid combination of this string. we count this while going back up the recursion.
eg:
In the above case, we use the dict as {"st", "sta", "a", "ck"}
We construct our trie ($ is the sentinel char, i.e. a char which is not in the dict):
$___s___t.___a.
|___a.
|___c___k.
the . represents that a word in the dict ends at that position.
We try to find the no of constructions of stack.
We start searching stack in the trie.
depth=0
$___s(*)___t.___a.
|___a.
|___c___k.
We see that we are at the end of one word, we start a new search with the remaining string ack from the top.
depth=0
$___s___t(*).___a.
|___a.
|___c___k.
Again we are at the end of one word in the dict. We start a new search for ck.
depth=1
$___s___t.___a.
|___a(*).
|___c___k.
depth=2
$___s___t.___a.
|___a.
|___c(*)___k.
We reach the end of stack and end of a word in the dict, hence we have 1 valid representation of stack.
depth=2
$___s___t.___a.
|___a.
|___c___k(*).
We go back to the caller of depth=2
No next char is available, we return to the caller of depth=1.
depth=1
$___s___t.___a.
|___a(*, 1).
|___c___k.
depth=0
$___s___t(*, 1).___a.
|___a.
|___c___k.
We move to next char. We see that we reached the end of one word in the dict, we launch a new search for ck in the dict.
depth=0
$___s___t.___a(*, 1).
|___a.
|___c___k.
depth=1
$___s___t.___a.
|___a.
|___c(*)___k.
We reach the end of the stack and a work in the dict, so another valid representation. We go back to the caller of depth=1
depth=1
$___s___t.___a.
|___a.
|___c___k(*, 1).
There are no more chars to proceed, we return with the result 2.
depth=0
$___s___t.___a(*, 2).
|___a.
|___c___k.
Note: The implementation is in C++, shouldn't be too hard to convert to Java and this implementation assumes that all chars are lowercase, it's trivial to extend it to both cases.
Sample code (full version):
/**
Node *base: head of the trie
Node *h : current node in the trie
string s : string to search
int idx : the current position in the string
*/
int count(Node *base, Node *h, string s, int idx) {
// step 3: found a valid combination.
if (idx == s.size()) return h->end;
int res = 0;
// step 2: we recursively start a new search.
if (h->end) {
res += count(base, base, s, idx);
}
// move ahead in the trie.
if (h->next[s[idx] - 'a'] != NULL) {
res += count(base, h->next[s[idx] - 'a'], s, idx + 1);
}
return res;
}
def cancons(target,wordbank, memo={}):
if target in memo:
return memo[target]
if target =='':
return 1
total_count =0
for word in wordbank:
if target.startswith(word):
l= len(word)
number_of_way=cancons(target[l:],wordbank,memo)
total_count += number_of_way
memo[target]= total_count
return total_count
if __name__ == '__main__':
word = "stackoverflow";
String= ["st", "ck","CAG","low","TC","rf","ove","a","sta"]
b=cancons(word,String,memo={})
print(b)
For instance, take the following list of Strings, disregarding the inverted commas:
"Hello"
"Hello!"
"I'm saying Hello!"
"I haven't said hello yet, but I will."
Now let's say I'd like to perform a certain operation on the characters of each word — for instance, say I'd like to reverse the characters, but keep the positions of the punctuation. So the result would be:
"olleH"
"olleH!"
"m'I gniyas olleH!"
"I tneva'h dias olleh tey, tub I lliw."
Ideally I'd like my code to be independent of the operation performed on the string (another example would be a random shuffling of letters), and independent of all punctuation—so hyphens, apostrophes, commas, full stops, en/em dashes, etc. all remain in their original positions after the operation is performed. This probably requires some form of regular expressions.
For this, I was thinking that I should save the indices and characters of all punctuation in a given word, perform the operation, and then re-insert all punctuation at their correct positions. However, I can't think of a way to do this, or a class to use.
I have a first attempt, but this unfortunately does not work with punctuation, which is the key:
jshell> String str = "I haven't said hello yet, but I will."
str ==> "I haven't said hello yet, but I will."
jshell> Arrays.stream(str.split("\\s+")).map(x -> (new StringBuilder(x)).reverse().toString()).reduce((x, y) -> x + " " + y).get()
$2 ==> "I t'nevah dias olleh ,tey tub I .lliw"
Has anyone got an idea how I might fix this? Thanks very much. There's no need for full working code—maybe just a signpost to an appropriate class I could use to perform this operation.
No need to use regex for this, and you certainly shouldn't use split("\\s+"), since you'd lose consecutive spaces, and the type of whitespace characters, i.e. the spaces of the result could be incorrect.
You also shouldn't use charAt() or anything like it, since that would not support letters from the Unicode Supplemental Planes, i.e. Unicode characters that are stored in Java strings as surrogate pairs.
Basic logic:
Locate start of word, i.e. start of string or first character following whitespace.
Locate end of word, i.e. last character preceding whitespace or end of string.
Iterating from beginning and end in parallel:
Skip characters that are not letters.
Swap the letters.
As Java code, with full Unicode support:
public static String reverseLettersOfWords(String input) {
int[] codePoints = input.codePoints().toArray();
for (int i = 0, start = 0; i <= codePoints.length; i++) {
if (i == codePoints.length || Character.isWhitespace(codePoints[i])) {
for (int end = i - 1; ; start++, end--) {
while (start < end && ! Character.isLetter(codePoints[start]))
start++;
while (start < end && ! Character.isLetter(codePoints[end]))
end--;
if (start >= end)
break;
int tmp = codePoints[start];
codePoints[start] = codePoints[end];
codePoints[end] = tmp;
}
start = i + 1;
}
}
return new String(codePoints, 0, codePoints.length);
}
Test
System.out.println(reverseLettersOfWords("Hello"));
System.out.println(reverseLettersOfWords("Hello!"));
System.out.println(reverseLettersOfWords("I'm saying Hello!"));
System.out.println(reverseLettersOfWords("I haven't said hello yet, but I will."));
System.out.println(reverseLettersOfWords("Works with surrogate pairs: 𝓐𝓑𝓒+𝓓 "));
Output
olleH
olleH!
m'I gniyas olleH!
I tneva'h dias olleh tey, tub I lliw.
skroW htiw etagorrus sriap: 𝓓𝓒𝓑+𝓐
Note that the special letters at the end are the first 4 shown here in column "Script (or Calligraphy)", "Bold", e.g. the 𝓐 is Unicode Character 'MATHEMATICAL BOLD SCRIPT CAPITAL A' (U+1D4D0), which in Java is two characters "\uD835\uDCD0".
UPDATE
The above implementation is optimized for reversing the letters of the word. To apply an arbitrary operation to mangle the letters of the word, use the following implementation:
public static String mangleLettersOfWords(String input) {
int[] codePoints = input.codePoints().toArray();
for (int i = 0, start = 0; i <= codePoints.length; i++) {
if (i == codePoints.length || Character.isWhitespace(codePoints[i])) {
int wordCodePointLen = 0;
for (int j = start; j < i; j++)
if (Character.isLetter(codePoints[j]))
wordCodePointLen++;
if (wordCodePointLen != 0) {
int[] wordCodePoints = new int[wordCodePointLen];
for (int j = start, k = 0; j < i; j++)
if (Character.isLetter(codePoints[j]))
wordCodePoints[k++] = codePoints[j];
int[] mangledCodePoints = mangleWord(wordCodePoints.clone());
if (mangledCodePoints.length != wordCodePointLen)
throw new IllegalStateException("Mangled word is wrong length: '" + new String(wordCodePoints, 0, wordCodePoints.length) + "' (" + wordCodePointLen + " code points)" +
" vs mangled '" + new String(mangledCodePoints, 0, mangledCodePoints.length) + "' (" + mangledCodePoints.length + " code points)");
for (int j = start, k = 0; j < i; j++)
if (Character.isLetter(codePoints[j]))
codePoints[j] = mangledCodePoints[k++];
}
start = i + 1;
}
}
return new String(codePoints, 0, codePoints.length);
}
private static int[] mangleWord(int[] codePoints) {
return mangleWord(new String(codePoints, 0, codePoints.length)).codePoints().toArray();
}
private static CharSequence mangleWord(String word) {
return new StringBuilder(word).reverse();
}
You can of course replace the hardcoded call to the either mangleWord method with a call to a passed-in Function<int[], int[]> or Function<String, ? extends CharSequence> parameter, if needed.
The result with that implementation of the mangleWord method(s) is the same as the original implementation, but you can now easily implement a different mangling algorithm.
E.g. to randomize the letters, simply shuffle the codePoints array:
private static int[] mangleWord(int[] codePoints) {
Random rnd = new Random();
for (int i = codePoints.length - 1; i > 0; i--) {
int j = rnd.nextInt(i + 1);
int tmp = codePoints[j];
codePoints[j] = codePoints[i];
codePoints[i] = tmp;
}
return codePoints;
}
Sample Output
Hlelo
Hlleo!
m'I nsayig oHlel!
I athen'v siad eohll yte, btu I illw.
srWok twih rueoatrsg rpasi: 𝓑𝓒𝓐+𝓓
I suspect there's a more efficient solution but here's a naive one:
Split sentence into words on spaces (note - if you have multiple spaces my implementation will have problems)
Strip punctuation
Reverse each word
Go through each letter, and insert character from reversed word AND insert punctuation from original word if necessary
public class Reverser {
public String reverseSentence(String sentence) {
String[] words = sentence.split(" ");
return Arrays.stream(words).map(this::reverseWord).collect(Collectors.joining(" "));
}
private String reverseWord(String word) {
String noPunctuation = word.replaceAll("\\W", "");
String reversed = new StringBuilder(noPunctuation).reverse().toString();
StringBuilder result = new StringBuilder();
for (int i = 0; i < word.length(); ++i) {
char ch = word.charAt(i);
if (!Character.isAlphabetic(ch) && !Character.isDigit(ch)) {
result.append(ch);
}
if (i < reversed.length()) {
result.append(reversed.charAt(i));
}
}
return result.toString();
}
}
So im trying the following challenge:
Using the Java language, have the function LetterChanges(str) take the str parameter being passed andmodify it using the following algorithm. Replace every letter in the string with the letter following it in thealphabet (ie. c becomes d, z becomes a). Then capitalize every vowel in this new string (a, e, i, o, u) and finally return this modified string.
This is my code
class LetterChange {
public static String LetterChanges(String str) {
String alphabet = "AbcdEfghIjklmnOpqrstUvwxyz";
char currentChar,letter;
int i = 0;
while (i < str.length())
{
currentChar = str.charAt(i);
for(int x = 0; x < alphabet.length(); x++)
{
letter = alphabet.charAt(x);
if (currentChar == letter){
str = str.replace(currentChar,alphabet.charAt(x+1));
i++;
}
}
}
when I run it it is returning the last char in string +1 letter in alphabet. for example if i was to run "bcd" it returns "EEE". I dont understand why its replacing all chars with the result of the loop for the last char.
When you go through the loop the first time you get
"bcd"--> "ccd"
Now, str.replace will turn this into "ddd" on next turn, then "EEE".
I.e., replace replaces every occurrence on each turn.
It is true that debugging it in the IDE will help you in the future!
Also, what if you had a lowercase vowel in your string?
public class Alphabet {
public static String LetterChanges(String str) {
String alphabet = "AbcdEfghIjklmnOpqrstUvwxyz";
char[] string = str.toLowerCase().toCharArray();
for (int i=0; i < string.length; i++) {
char d = alphabet.charAt(((alphabet.toLowerCase().indexOf(string[i]))+1) % 26);
string[i]=d;
}
return new String(string);
}
public static void main(String[] args) {
System.out.println(Alphabet.LetterChanges("aabb"));
}
}
alphabet.charAt(
((alphabet.toLowerCase().indexOf(string[i]))
+1) % 26)
1) use toLowerCase on the input and your string map to eliminate case problems
2) find character at index+1 in string map 'alphabet', treating it as a circular buffer using a modulus that takes z to a.
index 25 (z) + 1 == 26 --> 0 (A) because 26 is 0 mod 26 while index 0(A) + 1 = 1 --> 1 mod 26. It is only necessary to wrap the z to A while not changing the other 25 indices and is more efficient than branching with an "if" statement.
Does this solution help?
public static String letterChanges(String str) {
String alphabet = "AbcdEfghIjklmnOpqrstUvwxyz";
StringBuilder stringBuilder = new StringBuilder();
for (char letter : str.toCharArray()) {
if (alphabet.contains(Character.toString(letter))) {
int index = alphabet.indexOf(letter) + 1;
if (index >= 26) {
index = 0;
}
stringBuilder.append(alphabet.charAt(index));
}
}
return stringBuilder.toString();
}
The previous solution was hard to follow, so it's difficult to explain why it wasn't working without debugging through it to see where it goes wrong. It was easier to use a for-each loop to go through the str parameter and find matches using Java's provided methods like .indexOf and .charAt.
Also, Java uses lower camel case method naming, letterChanges instead of LetterChanges :)
Let me know if you have any questions.
You are getting that result because on every replacing you are re-setting the input string. I recommend you:
Better try with two different variables: Let the input variable be unmodified, and work on the output one.
Since strings are unmodifiable -as you already know- better declare them as arrays of char.
For the shake of optimization, base your algorithm on one single loop, which will iterate over the characters of the input string. For each character, decide if it is alphabetic or not, and in case it is, what character should it be replaced with.
I want a regex to match a single letter in a string (from A to Z in order):
It should find the letter 'A', if there are no 'A's, it should find the letter 'B', then 'C', and so on...
Examples ->
BCDAE
CBDE -> Since there's no 'A's, it matches with B
YXZ
BAAC -> Since there're two 'A's, it finds the leftmost
character first.
Extra Information:
I'd provide an example, as some users don't seem to like questions without code.
Given a lower case string remove k characters from that string. First
remove all letter 'a', followed by letter 'b', then 'c', etc..
.This was my solution:
public static String remove(String s, int k) {
for (int c : s.chars().sorted().limit(k).toArray())
s = s.replaceFirst(Character.toString((char) c), "");
return s;
}
But I'd like to try this with a regex like:
public static String remove(String s, int k) {
while (k-- > 0)
s = s.replaceFirst(MY_MAGIC_REGEX_STR, "");
return s;
}
Regex might not be the best tool suited for this problem. I think the easiest thing to do here is to just convert your input string to an array of characters, and then walk down that array, keeping track of what the minimum (smallest) character is:
public char findLowestChar(String input) {
char[] array = input.toCharArray();
char chr = 'Z'; // works so long as input is non-empty
for (int i=0; i < array.length; ++i) {
if (array[i] < chr) {
chr = array[i];
}
}
return chr;
}
I am assuming here that the input string would always have at least one letter A-Z in it. If not, and you also wanted to implement this inside a method, then you should also handle the empty input case.
Edit:
You just substantially changed your question. But it turns out the above code can still be part of the updated answer. You can now iterate k times, and at each step run the above code to find the lowest letter. Then, do a String#replaceAll to remove all occurrences of that letter.
String input = "BCDAE";
// remove k=4 characters, starting with (maybe) A, from the input string
for (int k=0; k < 4 && input.length() > 0; ++k) {
char lowest = findLowestChar(input);
input = input.replaceAll(String.valueOf(lowest), "");
}
The following regex works as desired:
(?i)A|B(?!.*[A-A])|C(?!.*[A-B])|D(?!.*[A-C])|E(?!.*[A-D])|F(?!.*[A-E])|G(?!.*[A-F])|H(?!.*[A-G])|I(?!.*[A-H])|J(?!.*[A-I])|K(?!.*[A-J])|L(?!.*[A-K])|M(?!.*[A-L])|N(?!.*[A-M])|O(?!.*[A-N])|P(?!.*[A-O])|Q(?!.*[A-P])|R(?!.*[A-Q])|S(?!.*[A-R])|T(?!.*[A-S])|U(?!.*[A-T])|V(?!.*[A-U])|W(?!.*[A-V])|X(?!.*[A-W])|Y(?!.*[A-X])|Z(?!.*[A-Y])
The regex consists of 26 terms (one term per letter) which are concatenated via the alternation-operator (|). The A(?!B) is the negative look ahead operator which match A if A is not followed by B.The (?i) simply triggers case insensitivity.
On the whole the regex finds first all A's from left to right, than all B's from left to right and so on.
Because of the length of the regex it is more comfortable to generate it programmatically:
// Generate regEx
String regEx = "(?i)" + "A" + "|";
for (char i = 'B'; i <= 'Z'; i++ ) {
regEx += i + "(?!.*[A-" + (char)(i-1) + "])" + "|";
}
regEx = regEx.substring(0, regEx.length() - 1);
System.out.println(regEx);
For the following example:
String example = "AAAZZZHHAAAZZHHHAAZZZHH";
// Output
while(example.length() != 0) {
System.out.println(example);
example = example.replaceFirst(regEx, "");
}
the output is:
AAAZZZHHAAAZZHHHAAZZZHH
AAZZZHHAAAZZHHHAAZZZHH
AZZZHHAAAZZHHHAAZZZHH
ZZZHHAAAZZHHHAAZZZHH
ZZZHHAAZZHHHAAZZZHH
ZZZHHAZZHHHAAZZZHH
ZZZHHZZHHHAAZZZHH
ZZZHHZZHHHAZZZHH
ZZZHHZZHHHZZZHH
ZZZHZZHHHZZZHH
ZZZZZHHHZZZHH
ZZZZZHHZZZHH
ZZZZZHZZZHH
ZZZZZZZZHH
ZZZZZZZZH
ZZZZZZZZ
ZZZZZZZ
ZZZZZZ
ZZZZZ
ZZZZ
ZZZ
ZZ
Z
I am having issues running my code correctly. I created a decryption method that is supposed to take a word and replace every 2 letters with each other. Unless the word is an odd amount, then I should leave the last letter alone. The issue is that I am only printing out the letters and not changing the actual data of the string.
static String ezDecrypt (String ezEncrypt){
//this variable holds the value of the String's length
int cl = ezEncrypt() ;
//if the length of the String is even then do this
if (ezEncrypt.length() % 2 == 0){
//for loop that begins at 0
//keeps looping until it reaches the end of the string
//each loop adds 2 to the loop
for(int i = 0; i < cl; i= i + 2) {
//will print out the second letter in the string
System.out.print(ezEncrypt.charAt(i + 1));
//will print out the first letter in the string
System.out.print(ezEncrypt.charAt(i));
}
}
//if the length of the word is an odd number, then
else if(ezEncrypt.length() % 2 != 0){
//loop through and do the same process as above
//except leave this loop will skip the last letter
for(int i = 0; i < cl-1; i= i + 2) {
//will print out the second letter in the string
System.out.print(ezEncrypt.charAt(i + 1));
//will print out the first letter in the string
System.out.print(ezEncrypt.charAt(i));
}
}
return ezEncrypt;
}
I understand you're trying to modify a String in order to decrypt it. Well, I got some news for you: the String class in java has been designed in such a way String objects are immutable. That means you can't alter their contents once you have created them. But don't worry, there are other ways to achieve what you have in mind.
For instance, you can get an array of chars from the received object by calling ezEncrypt.toCharArray(); you can modify the contents of an array so you will have to work with that, swapping the characters just like you're supposed to. Then, once the decryption is done, create another String object by using the constructor new String(char[] chars), passing your array as argument, and return it.
Something more or less like this:
static String ezDecrypt (String ezEncrypt){
//this variable holds the value of the String's length
int cl = ezEncrypt.length();
//an array holding each character of the originally received text
char[] chars = ezEncrypt.toCharArray();
//temporary space for a lonely character
char tempChar;
//Do your swapping here
if (ezEncrypt.length() % 2 == 0){ //Length is even
//for loop that begins at 0
//keeps looping until it reaches the end of the string
//each loop adds 2 to the loop
for(int i = 0; i < cl; i = i + 2) {
tempChar = chars[i];
chars[i] = chars[i+1];
chars[i+1] = tempChar;
}
} else { //Length is odd
//loop through and do the same process as above
//except leave this loop will skip the last letter
for(int i = 0; i < cl - 1; i = i + 2) {
tempChar = chars[i];
chars[i] = chars[i+1];
chars[i+1] = tempChar;
}
}
return new String(chars);
}
Hope this helps you.
Strings are immutable, so calling a method on the string will not change the string. It will only return a value derived from the string. You need to make a new empty string and start adding the return values to it character by character.