Searching if String differs by one character - java

I'm trying to determine if a word entered differs by one character in a text file. I have code that works, but unfortunately only for words that are two characters or less which obviously isn't very useful, and the code itself looks a bit messy. Here's what I have so far:
if(random.length() == word.length()){
for(int i = 0; i < random.length(); i++){
if( (word.charAt(i) == random.charAt(i))){
str += word+"\n";
count++;
}
}
}
With random being the word that was entered by the user, and word being the word to search for in the text file.
If I changed my second if statement to something along the lines of
if( (word.charAt(i) == random.charAt(i)) && (word.charAt(i -1) == random.charAt(i-1)))
and if I change int i to be = 1 instead, I seem to get more of what I'm looking to accomplish, but then my code is searching for only if the first two letters are the same and not if the last two are as well, which it should be doing.

I assume you need a function like this? I just wrote and tested it.
static boolean equals(String word1, String word2, int mistakesAllowed) {
if(word1.equals(word2)) // if word1 equals word2, we can always return true
return true;
if(word1.length() == word2.length()) { // if word1 is as long as word 2
for(int i = 0; i < word1.length(); i++) { // go from first to last character index the words
if(word1.charAt(i) != word2.charAt(i)) { // if this character from word 1 does not equal the character from word 2
mistakesAllowed--; // reduce one mistake allowed
if(mistakesAllowed < 0) { // and if you have more mistakes than allowed
return false; // return false
}
}
}
}
return true;
}

Your code seems to be working to me, you just may be interpreting its results incorrectly.
This may be more obvious:
int count = 0; if(random.length() == word.length()) {
for(int i = 0; i < random.length(); i++)
{
if( (word.charAt(i) != random.charAt(i) ))
{
if(count == 0)
{
System.out.println("Found first difference!");
}
if(count != 0)
{
System.out.println("Strings are more than one letter different!");
}
count++;
}
} }
If you want to check Strings of different lengths, you'll need to delete characters from the longer one until it's the same size as the shorter.
For example:
If String1 = "abc";
and String2 = "zzzabcdef";
You'll need to delete 6 characters from the second string and test for every combination of 6 characters deleted. So you would want to test the strings: def, cde, abc, zab, zza, zzz, zzb, zzc, zzd, zze, zzf, zaf, zae, zad, zac, zab, zza, zzf, zze, ..., ..., on and on, the list is of size 9 choose 6, so it's definitely not optimal or recommended.
You can however, check to see if a string which is one character longer than the other is just the other string with one added letter. To do this, you want a for loop to grab two substring from 0 to i, and from i+1 to the end. This will leave out the ith character, and looping for the size of the string - 1, will give you first the full string, then the string without the first letter, then missing the second letter, and so on. Then test that substring in the same fashion we did above.
Comment if this was not what you're looking for.
EDIT
To see how many words in a file are one letter different than a variable word, you need to loop through the file, getting each word. Then testing if that was string was one letter off. It would be something like this:
String testAgainst = "lookingForWordsOneLetterDifferentThanThisString";
int words = 0;
Scanner scan = new Scanner(fileName);
while(scan.hasNext())
{
String word = scan.next();
if( isOneDifferent(word, testAgainst) )
{
words++;
}
System.out.println("Number of words one letter different: " + words);
}
public boolean isOneDifferent(String word, String testAgainst)
{
if(word.length() != testAgainst.length())
{
return false;
}
int diffs = 0;
for(int i = 0; i < word.length(); i++)
{
if(word.charAt(i) != testAgainst.charAt(i))
{
diffs++;
}
if(diffs > 1)
{
return false;
}
}
if(diffs == 1)
{
return true;
}
else
{
return false;
}
}

Related

Duplicate output in Java

I'm new here and still learning. Today I learn find duplicate in string. From https://www.javatpoint.com/program-to-find-the-duplicate-characters-in-a-string, I try to learn complete code from web.
When string = "Great responsibility" the output will be:
Duplicate characters in a given string:
r
e
t
s
i
because it has duplicate character r e t s i
And when string is "great" the output is
Duplicate characters in a given string:
The output is blank because there are no duplicate characters, so I give a description "no duplicate" to define no character duplicate and the output goes like this
Duplicate characters in a given string:
no duplicates
no duplicates
no duplicates
no duplicates
no duplicates
This returns too many descriptions.
My code
public class DuplicateCharacters {
public static void main(String[] args) {
String string1 = "Great";
int count;
//Converts given string into character array
char string[] = string1.toCharArray();
System.out.println("Duplicate characters in a given string: ");
//Counts each character present in the string
for(int i = 0; i <string.length; i++) {
count = 1;
for(int j = i+1; j <string.length; j++) {
if(string[i] == string[j] && string[i] != ' ') {
count++;
//Set string[j] to 0 to avoid printing visited character
string[j] = '0';
}
}
//A character is considered as duplicate if count is greater than 1
if(count > 1 && string[i] != '0')
System.out.println(string[i]);
else
System.out.println("no duplicates");
}
}
}
How can I print only one description without repetition? I tried return 0; but it does not work.
Expected output
Duplicate characters in a given string:
no duplicates
Separate the logic for finding duplicates from how you report the findings to the user. Move the logic for finding the duplicates into a method. Pass the results of that output to another method. The main method invokes the first and passes the output to the second.
public static void main(String[] args) {
String s = .... whatever you are searching for duplicates in ....
reportDuplicates(findDuplicates(s)):
}
public static List<Character> findDuplicates(String s) {
... returns a List containing duplicates ...
}
public static void reportDuplicates(List<Character> duplicates) {
if (null == duplicates || duplicates.isEmpty()) {
... report no duplicates ...
} else {
... output the duplicates
}
}
Add a flag to your program that indicates whether there are duplicates or not. And after loop check whether this flag is true or false.
This method would look like below. I commented code where I updated it.
public static void main(String[] args) {
String string1 = "Great";
int count;
//Converts given string into character array
char string[] = string1.toCharArray();
// here is flag added
boolean noDuplicates = true;
System.out.println("Duplicate characters in a given string: ");
//Counts each character present in the string
for(int i = 0; i <string.length; i++) {
count = 1;
for(int j = i+1; j <string.length; j++) {
if(string[i] == string[j] && string[i] != ' ') {
count++;
//Set string[j] to 0 to avoid printing visited character
string[j] = '0';
}
}
//A character is considered as duplicate if count is greater than 1
if(count > 1 && string[i] != '0') {
System.out.println(string[i]);
//here is flag updated if duplicates are found
noDuplicates = false;
}
}
//here is flag check
if (noDuplicates) {
System.out.println("no duplicates");
}
}
And btw. Your algorithm has O(n^2) time complexity. You can figure out one that is better ;-)
It's normal your System.out.println("no duplicates"); is in your loop so each time a character is not duplicate you print "no duplicates".
You can defined a boolean that will become true if one duplicate it's found, like this :
public class DuplicateCharacters {
public static void main(String[] args) {
String string1 = "Great";
int count;
//Converts given string into character array
char string[] = string1.toCharArray();
System.out.println("Duplicate characters in a given string: ");
//Counts each character present in the string
Boolean dupCarac = false;
for(int i = 0; i <string.length; i++) {
count = 1;
for(int j = i+1; j <string.length; j++) {
if(string[i] == string[j] && string[i] != ' ') {
count++;
//Set string[j] to 0 to avoid printing visited character
string[j] = '0';
}
}
//A character is considered as duplicate if count is greater than 1
if(count > 1 && string[i] != '0'){
System.out.println(string[i]);
dupCarac = true;
}
}
if (!dupCarac){
System.out.println("no duplicates");
}
}
PS: Please put {} on your if and else.
You might find interesting the following approach of how you can do the same, using Streams more efficiently, without iterating through the same String multiple times.
String input = "Great responsibility";
Map<String, Long > map = Arrays.stream(input.split("")) //create a stream for each character in String
.collect((Collectors.groupingBy(item -> item, Collectors.counting()))) //Collect into a map all occurrences
.entrySet().stream().filter(e -> e.getValue() > 1 && !e.getKey().equals(" ")) //filter only duplicate occurrences and not empty spaces
.map(e -> Map.entry(e.getKey(), e.getValue() -1)) // keep count only of duplicate occurrences not total occurrences
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); //gather duplicates in a map
if (map.isEmpty()){
System.out.println("No duplicates found");
} else {
map.forEach((key, value) -> System.out.printf("%s appears %d more times in given string%n", key, value));
}

My code passes all of the given tests for CodingBat but doesn't pass "other tests". I'm not sure what my problem is

I'm working on CodingBat for class and can't figure out why my code isn't working. It passes all of the tests that it lists out but then doesn't pass the "other tests". If you could help me figure out what's wrong that would be really helpful. Here is the problem:
Given a string, count the number of words ending in 'y' or 'z' -- so the 'y' in "heavy" and the 'z' in "fez" count, but not the 'y' in "yellow" (not case sensitive). We'll say that a y or z is at the end of a word if there is not an alphabetic letter immediately following it. (Note: Character.isLetter(char) tests if a char is an alphabetic letter.)
Here are the tests that it listed and I passed.
countYZ("fez day")
countYZ("day fez")
countYZ("day fyyyz")
countYZ("day yak")
countYZ("day:yak")
countYZ("!!day--yaz!!")
countYZ("yak zak")
countYZ("DAY abc XYZ")
countYZ("aaz yyz my")
countYZ("y2bz")
countYZ("zxyx")
Here is the code I have so far. (I know I could have done it cleaner by converting to lower case but I only realized that afterwards.)
public int countYZ(String str) {
int count = 0;
str = str.toLowerCase();
for (int i=0; i<str.length()-1; i++) {
if (!Character.isLetter(str.charAt(i)) && i>0 && (str.substring(i-1,i).equals("y") || str.substring(i-1,i).equals("z")))
{ count++; }
}
if (str.endsWith("y") || str.endsWith("z")) { count++; }
return count;
}
The problem: The way you have set up your logic, your loop looks up until one character before the end of the String and then you check the last character with your if statement. However, your loop only effectively checks the char before i, so you are not checking a char. A String that this will fail on is:
"y z "
(Notice the extra space at the end. It can be any other non letter character, which is defined as the ending of the prior word)
This String should return 2, but returns 1. The easy fix is to change your loop to:
for (int i=0; i<str.length(); i++)
Perhaps a better approach:
Right now you have a mess of boolean conditions to see if the character before is a non alphabetic, if the character at i is alphabetic and so on. If I were you I would simply use toLower(), and then split on any non alphabetic characters. Then for each String in the split array, use the endsWith function to easily check if it ends with z or y. Here's one possible solution:
public int countYZ(String str) {
if(str == null) return 0;
str = str.toLowerCase();
int count = 0;
for(String s : str.split("[^a-z]")) {
if(s.endsWith("y") || s.endsWith("z")) {
count++;
}
}
return count;
}
Or java 8+ you can simply do:
public int countYZ(String str) {
if(str == null) return 0;
return (int)Arrays.stream(str.toLowerCase().split("[^a-z]"))
.filter(e -> e.endsWith("y") || e.endsWith("z"))
.count();
}
Which solves all the test case:
Link to problem
Cleaning up your code will make it be easier to read and to debug.
Don't Repeat Yourself! Anytime you find yourself writing the same piece of code you are introducing potential bugs due to simple typing errors. Instead, you can move all the checks into a single method, and use equalsIgnoreCase to check if a given character matches Y or Z:
public static boolean isYOrZ(String s) {
if(s.equalsIgnoreCase("y") || s.equalsIgnoreCase("z")) return true;
else return false;
}
Right now, your code fails for null strings and for empty strings, so add a check before doing any processing:
int count = 0;
if(str == null || str.length() == 0) //check null first or you will get NPE!
return count;
Finally, you can update your code with the new helper method you created. If you understand how regex works, it's easier to use String.split() to process your words:
public static int countYZ(String str) {
int count = 0;
if(str == null || str.length() == 0) {
return count;
}
String[] words = str.split("[^A-z]"); //"not an alphabetic letter immediately following it"
for(String word : words) {
if(word.length() > 0) { //sanity check
if(isYOrZ(word.substring(word.length()-1))) {
++count;
}
}
}
return count;
}
Now you can test, write as many weird test cases that you can think of. For example, a huge string of spaces, an empty string, etc. Here are a few I tried:
System.out.println(countYZ("heavY yummy TASTY yellow zed buzz"));
System.out.println(countYZ(""));
System.out.println(countYZ(null));
System.out.println(countYZ("fiZz yay"));
System.out.println(countYZ("zzzZZza yyy"));
System.out.println(countYZ("z"));
System.out.println(countYZ(" "));
System.out.println(countYZ("heavy&testy#!##BuzZ")); //3
Which gives:
4
0
0
2
1
1
0
3

Longest Palindromic Sub String

I am trying to solve a problem from leet code. I have written a method for that. This works perfectly in local Eclipse, but when I submitted this solution at leetcode it says time limit exceeded.
Can someone suggest to me something I can alter in the code below to make it work faster? I am also able put input string in this post.
Code:
public String longestPalindrome(String s) {
if(s.equals("")) return "";
if(s.length()==1) return s;
if(s.length()==2) {
if(s.charAt(0) == s.charAt(1))
return s;
}
char[] ch = s.toCharArray();
Set<Integer> set = new HashSet<Integer>();
int maxP=0;String maxPalin="";
for(int i =2;i < s.length();i++){
if((ch[i-1]==ch[i] || ch[i-2] == ch[i]) && !set.contains(i) ){
int loop;
if(ch[i-1]==ch[i]){
loop=i-1;
}
else{
loop=i-2;
}
for(int k =i,l=loop; l>=0 && k<s.length();k++,l--){
if(ch[k]!= ch[l]){
if(maxP < s.substring(l+1, k).length())
maxPalin=s.substring(l+1, k);
maxP=s.substring(l+1, k).length();
set.add(i);
i=2;
break;
}
if(l==0){
if(maxP < s.substring(0, k+1).length())
maxPalin=s.substring(0, k+1);
maxP=s.substring(0, k+1).length();
set.add(i);
i=2;
break;
}
if(k== s.length()-1){
if(maxP < s.substring(l, s.length()).length())
maxPalin=s.substring(l, s.length());
maxP=s.substring(l, s.length()).length();
set.add(i);
i=2;
break;
}
}
}
}
return maxPalin;
}
Input:
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
Loop over the characters, for all positions check if the next character is the same as the current (e.g. 'f','f') or the character after and the character before are the same (e.g. 'f','g','f').
If that is the case, loop backward and forward and compare the characters until you find two that don't match.
Compare the length of the string with the longest you've found so far, if it is longer, save it.
Go to the next character.
You can also exit the loop if you have found a palindrom where length/2 is larger than the number of characters left.

Returning a string minus a specific character between specific characters

I am going through the Java CodeBat exercises. Here is the one I am stuck on:
Look for patterns like "zip" and "zap" in the string -- length-3, starting with 'z' and ending with 'p'. Return a string where for all such words, the middle letter is gone, so "zipXzap" yields "zpXzp".
Here is my code:
public String zipZap(String str){
String s = ""; //Initialising return string
String diff = " " + str + " "; //Ensuring no out of bounds exceptions occur
for (int i = 1; i < diff.length()-1; i++) {
if (diff.charAt(i-1) != 'z' &&
diff.charAt(i+1) != 'p') {
s += diff.charAt(i);
}
}
return s;
}
This is successful for a few of them but not for others. It seems like the && operator is acting like a || for some of the example strings; that is to say, many of the characters I want to keep are not being kept. I'm not sure how I would go about fixing it.
A nudge in the right direction if you please! I just need a hint!
Actually it is the other way around. You should do:
if (diff.charAt(i-1) != 'z' || diff.charAt(i+1) != 'p') {
s += diff.charAt(i);
}
Which is equivalent to:
if (!(diff.charAt(i-1) == 'z' && diff.charAt(i+1) == 'p')) {
s += diff.charAt(i);
}
This sounds like the perfect use of a regular expression.
The regex "z.p" will match any three letter token starting with a z, having any character in the middle, and ending in p. If you require it to be a letter you could use "z[a-zA-Z]p" instead.
So you end up with
public String zipZap(String str) {
return str.replaceAll("z[a-zA-Z]p", "zp");
}
This passes all the tests, by the way.
You could make the argument that this question is about raw string manipulation, but I would argue that that makes this an even better lesson: applying regexes appropriately is a massively useful skill to have!
public String zipZap(String str) {
//If bigger than 3, because obviously without 3 variables we just return the string.
if (str.length() >= 3)
{
//Create a variable to return at the end.
String ret = "";
//This is a cheat I worked on to get the ending to work easier.
//I noticed that it wouldn't add at the end, so I fixed it using this cheat.
int minusAmt = 2;
//The minus amount starts with 2, but can be changed to 0 when there is no instance of z-p.
for (int i = 0; i < str.length() - minusAmt; i++)
{
//I thought this was a genius solution, so I suprised myself.
if (str.charAt(i) == 'z' && str.charAt(i+2) == 'p')
{
//Add "zp" to the return string
ret = ret + "zp";
//As long as z-p occurs, we keep the minus amount at 2.
minusAmt = 2;
//Increment to skip over z-p.
i += 2;
}
//If it isn't z-p, we do this.
else
{
//Add the character
ret = ret + str.charAt(i);
//Make the minus amount 0, so that we can get the rest of the chars.
minusAmt = 0;
}
}
//return the string.
return ret;
}
//If it was less than 3 chars, we return the string.
else
{
return str;
}
}

Counting dashes within 2 separate strings, returning true if there are the same amount of dashes in the same positions of each string?

I have to write a program in Java that compares two strings for dashes. The test only returns true if each string has the same amount of dashes in the same position in the strings.
Example:
Comparing the following two strings
String string1 = "two-twenty-nine"
String string2 = "ten-fourty-five and I'm hungry."
with the above criteria would return true. It doesn't matter whether one string is longer than the other.
Any assistance would be appreciated!
I have tried:
- converting the strings to char arrays and then comparing the indexes
- Using String.indexOf() for each string, and then creating a variable int newStart = String.indexOf() with the index of the previous dash as the new starting point to look from
` public static void sameDashes(String string1, String string2) {
int count = 0;
char index1 = ' ';
char index2 = ' ';
char dash = '-';
char[] string1Array = string1.toCharArray();
char[] string2Array = string2.toCharArray();
while (count < string1Array.length && count < string2Array.length) {
if (string1Array[index1] == dash && string2Array[index2] == dash) {
System.out.println("true");
}
}
}
Since I suspect this is homework, I'm just going to outline a solution.
Iterate through string1 using the indexOf method, and get a List of Integers representing the positions of the dashes.
Iterate through string2 in the same manner. (You could call a general method twice. Once with string1 and once with string2.)
Compare your Lists, and see if they have the same size().
If they're the same size, loop through both lists and see if the positions are the same.
Super simple using String.split:
Pad both strings with a single character on each end (i.e. str = " " + str + " ").
Call .split("-") on both strings and store the resulting arrays.
If the arrays are different lengths then the strings don't match.
Otherwise, compare the lengths of the corresponding strings in each array, and if the lengths of any pair don't match then the strings don't match. Except if it was the last pair of strings—then they can be different lengths and they still match (i.e. just ignore the last pair of strings—you don't need to compare the strings at the last index in the arrays).
Otherwise, the two strings match.
A simple algorithm to solve this problem would be by using indexOf() and charAt() and looping through one of the string's instances of -.
Start the index-offset at 0.
Get the next position of a - in string1 from the given offset.
Check if string2 contains characters at the position found, if so, check if it's a -. If it's not a -, the check has failed.
Set the index-offset to the next - in string1 and repeat #2.
After all -'s have been found in string1, check if string2 contains a - at a point further in the string than string1 contained. If it does, the check has failed. If it doesn't, the check has passed!
Sample proof-of-concept:
int index = string1.indexOf("-", 0);
while (index > 0) {
// if string2 doesn't contain a dash at the current position, return false
if ((string2.length() <= index) || (string2.charAt(index) != "-")) return false;
index = string1.indexOf("-", (index + 1));
}
// do one last final check to see if string2 contains a dash that's further than string1's last dash
return !(string2.indexOf("-", string1.lastIndexOf("-") + 1) > 0);
I'll place an algorithm for this problem:
Check which String is the shortest: s1 or s2. Let's call the shortes string ss.
With ss, use String#indexOf to get the next position of the dash (or w/e char you need).
With the position, check in that position of the other String if it has a dash (or the character you're looking for). You can use String#charAt method for this purpose.
If the characters match, keep the loop until you get the index -1 (this means the character isn't in ss anymore).
Check if the other string contains the character from the last valid index. If there are no more, then both Strings have the same amount of dashes. no otherwise.
In this solution, there is no need to use an additional array to check the positions.
Try this:
package samedashes;
import java.io.IOException;
import java.util.Scanner;
public class SameDashes {
public static void main(String[] args) throws IOException {
sameDash();
}
public static void sameDash() {
String s1;
String s2;
char dash = '-';
float cnt = 0;
float s1dash = 0;
float s2dash = 0;
float totaldash1 = 0;
int minlength=0;
Scanner in = new Scanner(System.in);
System.out.print("Enter a string->");
s1 = in.nextLine();
System.out.print("Enter a string->");
s2 = in.nextLine();
if(s1.length()<s2.length()){
minlength=s1.length();
}else{
minlength=s2.length();
}
for (int i = 0; i < s1.length(); i++) {
if (s1.charAt(i) == dash) {
s1dash++;
}
}
for (int i = 0; i < s2.length(); i++) {
if (s2.charAt(i) == dash) {
s2dash++;
}
}
System.out.println("String1 dashes->" + s1dash);
System.out.println("String2 dashes->" + s2dash);
totaldash1 = s1dash + s2dash;
if (totaldash1 % 2 == 0 && s1.contains("-") && s2.contains("-")) {
for (int i = 0; i < minlength; i++) {
if (s1.charAt(i) == dash && s2.charAt(i) == dash) {
cnt++;
}
}
if (cnt >=1) {
System.out.println("true");
} else {
System.out.println("false");
}
} else {
System.out.println("false");
}
}
}

Categories