So I have a program that counts the number of occurrences of each letter in a string, and for that I use
int[] charAmount = new int[30];
for(int i = 0; i<text.length(); i++){
char sign = text.charAt(i);
int value = sign;
if(value >= 97 && value <= 122){
charAmount[value-97]++; // 97 = 'a'
}
This works fine, but I also need to cover the letters 'æ' (230), 'ø' (248) & 'å' (229). How can I "assign" those three letters to the 26, 27 & 29th index of the charAmount array without using if tests or a switch?
EDIT: The code presented above is not the whole block, I also have a switch for the letters in question, but I am looking for a better solution.
BONUS PROBLEM: When I try to enter a string like "æææ" or something, the value of 'æ' is suddenly 8216. I use a Scanner to read the input.
Try this after your if:
else if (value == 230)
charAmount[26]++;
else if (value == 248)
charAmount[27]++;
else if (value == 229)
charAmount[29]++;
You can also do an array of the chars and their associations, the array will look like this:
spChars_to_Chars =
0 => 96
1 => 97
...
229 => 29
230 => 26
...
248 => 27
And then just do this in your if:
charAmount[spChars_to_Chars[value]]++;
If you can use external libraries, I would rather try using Apache commons. They provide a function to count the matches of a given substring (your characters) in a bigger string:
StringUtils.countMatches(...)
Related
I'm doing some practice on HackerRank and I'm stuck on a problem. The problem is:
Julius Caesar protected his confidential information by encrypting it using a cipher. Caesar's cipher shifts each letter by a number of letters. If the shift takes you past the end of the alphabet, just rotate back to the front of the alphabet. In the case of a rotation by 3, w, x, y and z would map to z, a, b and c.
Original alphabet: abcdefghijklmnopqrstuvwxyz
Alphabet rotated +3: defghijklmnopqrstuvwxyzabc
Example
input:
11
middle-Outz
2
output:
okffng-Qwvb
I pass this test case, but I don't pass the rest of the test cases and it won't tell me which ones. What am I missing in my logic?
public static String caesarCipher(String s, int k) {
String newString = "";
// Write your code here
for (int i=0; i<s.length(); i++) {
char currentChar = s.charAt(i);
int currentAscii = currentChar;
if (currentAscii <65|| currentAscii>122 || (currentAscii>90 && currentAscii < 97)) {
newString += currentChar;
} else {
currentAscii = currentChar+k;
if (currentAscii > 122) {
currentAscii = Math.abs(currentAscii-26);
}
newString += (char) currentAscii;
}
}
return newString;
}
.
Here is an ASCII chart.
Your rotate algorithm is:
If the value is below 65 (less than 'A' in that chart), or above 122 (more than 'z' in that chart), do nothing to the character and just copy it verbatim.
In all other cases, add k, e.g. 11. This goes wrong, immediately. Note how for example [ is in between 65 and 122. Adding 11 to [ is nonsensical. [ should obviously be treated the same as, say, < is treated (which is less than 65).
Of course, adding 11 isn't what 'rotating' is all about: If adding 11 pushes it 'beyond' the z or Z, you want to snap around back to the a or A. Your code does that, but only if the end result is above 'z'. You forgot about 'Z'.
For example, adding 1 to Z (Which is value 90) gets you [ (the character with value 91). Given that 91 is not above 122, you don't modify this at all.
Add more test cases, especially involving capitals, preferably Z. You'll see the problem immediately.
The solution involves getting rid of the 65/122 dichotomy; instead it's 2 ranges: 65-90 and 97-122. For what it is worth, int a = (int) 65; and int a = (int) 'a'; is exactly the same code, and the latter is far more readable, so you might want to use that. There is no need to write any actual number in this code anywhere, except possibly '26'.
I got a problem and I think it is in comparing a char with a number.
String FindCountry = "BB";
Map<String, String> Cont = new HashMap <> ();
Cont.put("BA-BE", "Angola");
Cont.put("9X-92", "Trinidad & Tobago");
for ( String key : Cont.keySet()) {
if (key.charAt(0) == FindCountry.charAt(0) && FindCountry.charAt(1) >= key.charAt(1) && FindCountry.charAt(1) <= key.charAt(4)) {
System.out.println("Country: "+ Cont.get(key));
}
}
In this case the code print "Angola", but if
String FindCountry = "9Z"
it doesn't print anything. I am not sure I think the problem is in that it can't compare that is '2' greater than 'Z'. In that example, I got only two Cont.put(), but in my file, I got much more and a lot of them are not only with chars. I got a problem with them.
What is the smartest and best way to compare char with a number ? Actually, if I set a rule like "1" is greater than "Z" it will be okay because I need this way of greater: A-Z-9-0.
Thanks!
You can use a lookup "table", I used a String:
private static final String LOOKUP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
And then compare the chars with indexOf(), but it seems messy and could probably be achieved more easily, I just can't come up with something easier at the moment:
String FindCountry = "9Z";
Map<String, String> Cont = new HashMap<>();
Cont.put("BA-BE", "Angola");
Cont.put("9X-92", "Trinidad & Tobago");
for (String key : Cont.keySet()) {
if (LOOKUP.indexOf(key.charAt(0)) == LOOKUP.indexOf(FindCountry.charAt(0)) &&
LOOKUP.indexOf(FindCountry.charAt(1)) >= LOOKUP.indexOf(key.charAt(1)) &&
LOOKUP.indexOf(FindCountry.charAt(1)) <= LOOKUP.indexOf(key.charAt(4))) {
System.out.println("Country: " + Cont.get(key));
}
}
If you only use the characters A-Z and 0-9, you could add a conversion method in between which will increase the values of the 0-9 characters so they'll be after A-Z:
int applyCharOrder(char c){
// If the character is a digit:
if(c < 58){
// Add 43 to put it after the 'Z' in terms of decimal unicode value:
return c + 43;
}
// If it's an uppercase letter instead: simply return it as is
return c;
}
Which can be used like this:
if(applyCharOrder(key.charAt(0)) == applyCharOrder(findCountry.charAt(0))
&& applyCharOrder(findCountry.charAt(1)) >= applyCharOrder(key.charAt(1))
&& applyCharOrder(findCountry.charAt(1)) <= applyCharOrder(key.charAt(4))){
System.out.println("Country: "+ cont.get(key));
}
Try it online.
Note: Here is a table with the decimal unicode values. Characters '0'-'9' will have the values 48-57 and 'A'-'Z' will have the values 65-90. So the < 58 is used to check if it's a digit-character, and the + 43 will increase the 48-57 to 91-100, putting their values above the 'A'-'Z' so your <= and >= checks will work as you'd want them to.
Alternatively, you could create a look-up String and use its index for the order:
int applyCharOrder(char c){
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".indexOf(c);
}
Try it online.
PS: As mentioned in the first comment by #Stultuske, variables are usually in camelCase, so they aren't starting with an uppercase letter.
As the others stated in the comments, such mathematical comparison operations on characters are based on the actual ASCII values of each char. So I'd suggest you refactor your logic using the ASCII table as reference.
I'm using a for loop to parse a string, like so:
for (int i = 1; i < s.length(); ++i)
The string is all lowercase, and if either the current character or previous character is not a letter, I want to avoid it, like so:
if (s.charAt(i) - 97 < 0 || s.charAt(i) - 97 > 25 || s.charAt(i-1) < 0 || s.charAt(i-1) > 25)
{
continue
}
The first string I input is "marty," yet the if statement is registering true. I've confirmed that every single one of the four boolean values evaluates to false, yet the if statement itself is apparently true for every single letter in every single string.
I even made four separate bool variables, one for each parameter, confirmed they're all false, then OR'd them all together. Also false. Yet the if statement fires every single time.
I really don't know what I'm missing here. Can anyone help me?
You are using - 97 to subtract out 'a' so you can compare the character to the range 0-25. But you only did that for the first 2 conditions. Continue subtracting 97 for the last 2 conditions, which are on the "previous" character.
I'm trying to make a simple "translation" project as i'm still learning java. But something went wrong and I've spent many hours and still no idea how to fix it. Here is the code:
public static void main(String[] args)
{
char[] abcCode = {'!','#','#','$','%','^','&','*','(',')','_','-','+','=','|','`','~','}','{','[',']','"',';',':','>','<','?','/','\''};
char[] numCode = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','x','y','z'};
String src = args[0];
String result = "";
for(int i=0; i < src.length();i++)
{
char ch = src.charAt(i);
if ('a' <= ch && ch <= 'z')
result += abcCode[ch-'a'];
else if (abcCode[0] <= ch && ch <= abcCode[15])
result += numCode[ch-abcCode[0]];
}
System.out.println("src: "+src);
System.out.println("result: "+result);}}
For example if I type "lol" I will get "-|-", which is what I want. But the opposite never works, I'm supposed to type -|- and get "lol". Also if I type "#" and a few others I will get an error. What's the problem? Any lights?
Your problem is that the abcCode array is populated with things which, when converted to numbers, do not start at one value and go up by one in successive elements. I put the following code just after the declaration of the arrays:
for (char c: abcCode)
{
System.out.println(c - args[0].charAt(0));
}
return;
and got the following output:
-91
-60
-89
-88
-87
-30
-86
-82
-84
-83
-29
-79
-81
-63 0
-28 2 1
-1
-33
-31
-90
-65
-66
-62
-64
-61
-77
-85
(In my output, each number was on a different line, but I've forgotten how to force that here in the answer.)
As you can see, the resulting numbers are not valid indices into the other array.
Specification for a syllable:
Each group of adjacent vowels (a, e, i, o, u, y) counts as one syllable (for example, the "ea" in "real" contributes one syllable, but the "e...a" in "regal" counts as two syllables). However, an "e" at the end of a word doesn't count as a syllable. Also each word has at least one syllable, even if the previous rules give a count of zero.
My countSyllables method:
public int countSyllables(String word) {
int count = 0;
word = word.toLowerCase();
for (int i = 0; i < word.length(); i++) {
if (word.charAt(i) == '\"' || word.charAt(i) == '\'' || word.charAt(i) == '-' || word.charAt(i) == ',' || word.charAt(i) == ')' || word.charAt(i) == '(') {
word = word.substring(0,i)+word.substring(i+1, word.length());
}
}
boolean isPrevVowel = false;
for (int j = 0; j < word.length(); j++) {
if (word.contains("a") || word.contains("e") || word.contains("i") || word.contains("o") || word.contains("u")) {
if (isVowel(word.charAt(j)) && !((word.charAt(j) == 'e') && (j == word.length()-1))) {
if (isPrevVowel == false) {
count++;
isPrevVowel = true;
}
} else {
isPrevVowel = false;
}
} else {
count++;
break;
}
}
return count;
}
The isVowel method which determines if a letter is a vowel:
public boolean isVowel(char c) {
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
return true;
} else {
return false;
}
}
According to a colleague, this should result in 528 syllables when used on this text, but I can seem to get it to equal that and I don't know which of us is correct. Please help me develop my method into the correct algorithm or help show this is correct. Thank you.
One of the problem might be that you call to lover case method on the input, but you do not assign it.
So if you change
word.toLowerCase();
to
word = word.toLowerCase();
will help for sure.
I have just invented a new way to count syllables in Java.
My new library, The Lawrence Style Checker, can be viewed here: https://github.com/troywatson/Lawrence-Style-Checker
I counted your syllables for every word using my program and displayed the results here: http://pastebin.com/LyiBTcbb
With my dictionary method of counting syllables I got: 528 syllables total.
This is the exact number the questioner gave of the correct number of syllables. Yet I still dispute this number for reasons described below:
Strike rate: 99.4% correct
Words wrong: 2 / 337 words
Words wrong and wrong syllable counts: {resinous: 4, aardwolf: 3}
Here is my code:
Lawrence lawrence = new Lawrence();
// Turn the text into an array of sentences.
String sentences = ""
String[] sentences2 = sentences.split("(?<=[a-z])\\.\\s+");
int count = 0;
for (String sentence : sentences2) {
sentence = sentence.replace("-", " "); // split double words
for (String word : sentence.split(" ")) {
// Get rid of punctuation marks and spaces.
word = lawrence.cleanWord(word);
// If the word is null, skip it.
if (word.length() < 1)
continue;
// Print out the word and it's syllable on one line.
System.out.print(word + ",");
System.out.println(lawrence.getSyllable(word));
count += lawrence.getSyllable(word);
}
}
System.out.println(count);
bam!
This should be easily doable with some Regex:
Pattern p = Pattern.compile("[aeiouy]+?\w*?[^e]");
String[] result = p.split(WHAT_EVER_THE_INPUT_IS);
result.length
Please note, that it is untested.
Not a direct answer (and I would give you one if I thought it was constructive, my count is about 238 in the last try) but I will give you a few hints that will be fundamental to creating the answer:
Divide up your problem: Read lines, then split the lines up into words, then count the syllables for each word. Afterwords, count them up for all the lines.
Think about the order of things: first find all the syllables, and count each one by "walking" through the word. Factor in the special cases afterwards.
During design, use a debugger to step through your code. Chances are pretty high you make common mistakes like the toUpperCase() method. Better find those errors, nobody will create perfect code the first time around.
Print to console (advanced users use a log and keep the silenced log lines in the final program). Make sure to mark the println's using comments and remove them from the final implementation. Print things like line numbers and syllable counts so you can visually compare them with the text.
If you have advanced a bit, you may use Matcher.find (regular expressions) using a Pattern to find the syllables. Regular expressions are difficult beasts to master. One common mistake is have them do too much in a go.
This way you can quickly scan the text. One of the things you quickly will find out is that you will have to deal with the numbers in the text. So you need to check if a word is actually a word, otherwise, by your rules, it will have at least a single syllable.
If you have the feeling you are repeating things, like the isVowel and String.contains() methods using the same set of characters, you are probably doing something wrong. Repetition in source code is code smell.
Using regexps, I counted about 238 (in the 4th go), but I haven't really checked each and every syllable (of course).
1 14
2 17
3 17
4 15
5 15
6 14
7 16
8 19
9 17
10 17
11 16
12 19
13 18
14 15
15 18
16 15
17 16
18 17
19 16
20 17
21 17
22 19
23 17
24 16
25 17
26 17
27 16
28 17
29 15
30 17
31 19
32 23
33 0
--- total ---
538
I would strongly suggest that you use Java's String API to its full ability. For example, consider String.split(String regex):
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#split%28java.lang.String%29
This takes a String, and a regular expression, then returns an array of all the substrings, using your regular expression as a delimeter. If you make your regular expression match all consonants or whitespace, then you will end up with an array of Strings which are either empty (and therefore do not represent a consonant) or a sequence of vowels (which do represent a consonant). Count up the latter, and you will have a solution.
Another alternative which also takes advantage of the String API and regular expressions is replaceAll:
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#replaceAll%28java.lang.String,%20java.lang.String%29
In this case, you want a regular expression which takes the form [optional anything which isn't a vowel][one or more vowels][optional anything which isn't a vowel]. Run this regular expression on your String, and replace it with a single character (eg "1"). The end result is that each syllable will be replaced by a single character. Then all you need to do is String.length() and you'll know how many syllables you had.
Depending on the requirements of your solution, these may not work. If this is a homework question relating to algorithm design, this is almost certainly not the preferred answer, but it does have the benefit of being concise and makes good use of the built-in (and therefore highly optimized) Java APIs.
private static int countSyllables(String word)
{
//System.out.print("Counting syllables in " + word + "...");
int numSyllables = 0;
boolean newSyllable = true;
String vowels = "aeiouy";
char[] cArray = word.toCharArray();
for (int i = 0; i < cArray.length; i++)
{
if (i == cArray.length-1 && Character.toLowerCase(cArray[i]) == 'e'
&& newSyllable && numSyllables > 0) {
numSyllables--;
}
if (newSyllable && vowels.indexOf(Character.toLowerCase(cArray[i])) >= 0) {
newSyllable = false;
numSyllables++;
}
else if (vowels.indexOf(Character.toLowerCase(cArray[i])) < 0) {
newSyllable = true;
}
}
//System.out.println( "found " + numSyllables);
return numSyllables;
}
Another implementation can be found at below pastebin link:
https://pastebin.com/q6rdyaEd
This is my implementation for counting syllables
protected int countSyllables(String word)
{
// getNumSyllables method in BasicDocument (module 1) and
// EfficientDocument (module 2).
int syllables = 0;
word = word.toLowerCase();
if(word.contains("the ")){
syllables ++;
}
String[] split = word.split("e!$|e[?]$|e,|e |e[),]|e$");
ArrayList<String> tokens = new ArrayList<String>();
Pattern tokSplitter = Pattern.compile("[aeiouy]+");
for (int i = 0; i < split.length; i++) {
String s = split[i];
Matcher m = tokSplitter.matcher(s);
while (m.find()) {
tokens.add(m.group());
}
}
syllables += tokens.size();
return syllables;
}
It works fine for me.