Related
I am doing the following programming exercise: Merged String Checker
1) I have tried the following code:
import java.util.*;
public class StringMerger {
public static boolean isMerge(String s, String part1, String part2) {
System.out.println("\n\ns: "+s);
System.out.println("part1: "+part1);
System.out.println("part2: "+part2);
if(!s.isEmpty() && part1.isEmpty() && part2.isEmpty()) return false;
if( ( part1==null || part1.isEmpty() && part2.equals(s) ) || part2==null || part2.isEmpty() && part1.equals(s) ){
return true;
}
/*Check if complete words from s are in part1 or part2*/
List<String> sList = new ArrayList(Arrays.asList(s.split(" ")));
List<String> part1List = new ArrayList(Arrays.asList(part1.split(" ")));
List<String> part2List = new ArrayList(Arrays.asList(part2.split(" ")));
System.out.println("sList: "+Arrays.toString(sList.toArray()));
System.out.println("part1List: "+Arrays.toString(part1List.toArray()));
System.out.println("part2List: "+Arrays.toString(part2List.toArray()));
for(Iterator<String> it = sList.iterator(); it.hasNext(); ){
String word = it.next();
if(word.equals(part1List.get(0))){
it.remove();
part1List.remove(0);
System.out.println("sList: "+Arrays.toString(sList.toArray()));
System.out.println("part1List: "+Arrays.toString(part1List.toArray()));
}else if(word.equals(part2List.get(0))){
it.remove();
part2List.remove(0);
System.out.println("sList: "+Arrays.toString(sList.toArray()));
System.out.println("part2List: "+Arrays.toString(part2List.toArray()));
}
}
s=String.join(" ",sList);
part1=String.join(" ",part1List);
part2=String.join(" ",part2List);
System.out.println("\n\ns: "+s);
System.out.println("part1: "+part1);
System.out.println("part2: "+part2);
/*Check if s first character is part1 or part2 initial character*/
for(char letter : s.toCharArray()){
System.out.println("letter: "+letter);
System.out.println("part1: "+part1);
System.out.println("part2: "+part2);
if(!part1.isEmpty() && letter == part1.charAt(0)){
part1 = part1.substring(1);
System.out.println("part1: "+part1);
s = s.substring(1);
}else if(!part2.isEmpty() && letter==part2.charAt(0)){
part2 = part2.substring(1);
System.out.println("part2: "+part2);
s = s.substring(1);
}
System.out.println("s: "+s);
System.out.println("s.substring(0,part1.length()): "+s.substring(0,part1.length()));
if(s.substring(0,part1.length()).equals(part1)){
s=s.substring(part1.length());
part1="";
System.out.println("are equal, s: "+s);
}else if(s.substring(0,part2.length()).equals(part2)){
s=s.substring(part2.length());
part2="";
System.out.println("are equal, s: "+s);
}
if(s.isEmpty() || (part1.length()==0 && part2.length()==0) ) break;
}
System.out.println("\n\ns: "+s);
System.out.println("part1: "+part1);
System.out.println("part2: "+part2);
return s.isEmpty() && part1.isEmpty() && part2.isEmpty();
}
}
And I would like you to explain: why does it fail the following testβ½
import org.junit.Test;
import static org.junit.Assert.*;
public class StringMergerTest {
#Test
public void suffledPartsLetters(){
assertTrue("",StringMerger.isMerge("Can we merge it? Yes, we can!","nwe me?s, e cn","Ca erg it Yewa!"));
}
}
I have identified in the trace where is behaves unexpectedly:
letter: **r**
part1: ?s, e cn
part2: e**r**g it Yewa!
s: rge it? Yes, we can!
s.substring(0,part1.length()): rge it?
letter: **g**
part1: ?s, e cn
part2: er**g** it Yewa!
s: rge it? Yes, we can!
s.substring(0,part1.length()): rge it?
I understand that letter r and g are not being detected because of the code just checks if it is the first character in part1 or part2.
However I do not fully understand how could we fix the previous code to let it handle this case, could you help me please?
Besides I have also researched and found this post which describes some exercises' javascript solutions:
CodeWars/ Merged String Checker
I tried to write the recursive one without looking at the solution, and I came up with:
public class StringMerger {
public static boolean isMerge(String s, String part1, String part2) {
System.out.println("\n\ns: "+s);
System.out.println("part1: "+part1);
System.out.println("part2: "+part2);
if(s.length()!= (part1.length()+part2.length()) ){
System.out.println("lengths are different");
return false;
}
if(s.length()==0) {
System.out.println("s.length is 0");
return true;
}
if(part1.length()>0 && s.charAt(0)==part1.charAt(0)){
System.out.println("s first char is part1 first char");
isMerge(s.substring(1),part1.substring(1),part2);
}
if(part2.length()>0 && s.charAt(0)==part2.charAt(0)){
System.out.println("s first char is part2 first char");
isMerge(s.substring(1),part1,part2.substring(1));
}
return false;
}
}
Why does the previous one fail the following tests?
import org.junit.Test;
import static org.junit.Assert.*;
public class StringMergerTest {
#Test
public void normalHappyFlow() {
assertTrue("codewars can be created from code and wars", StringMerger.isMerge("codewars", "code", "wars"));
assertTrue("codewars can be created from cdwr and oeas", StringMerger.isMerge("codewars", "cdwr", "oeas"));
assertFalse("Codewars are not codwars", StringMerger.isMerge("codewars", "cod", "wars"));
}
#Test
public void suffledPartsLetters(){
assertTrue("",StringMerger.isMerge("Can we merge it? Yes, we can!","nwe me?s, e cn","Ca erg it Yewa!"));
}
}
I expected that when all letters are matched with part1 or part2 letters, and s is empty with length 0, it would output true.
However it outputs false even when it detects s.length is 0.
The trace is:
s: codewars
part1: code
part2: wars
s first char is part1 first char
s: odewars
part1: ode
part2: wars
s first char is part1 first char
s: dewars
part1: de
part2: wars
s first char is part1 first char
s: ewars
part1: e
part2: wars
s first char is part1 first char
s: wars
part1:
part2: wars
s first char is part2 first char
s: ars
part1:
part2: ars
s first char is part2 first char
s: rs
part1:
part2: rs
s first char is part2 first char
s: s
part1:
part2: s
s first char is part2 first char
s:
part1:
part2:
s.length is 0
How could we also fix the previous code? And why does it fails to pass the tests?
I have also read:
Best way to convert an ArrayList to a string
ConcurrentModificationException for ArrayList
java : remove words from ArrayList<String>
Removing items from a list
Converting array to list in Java
Checking if a string is empty or null in Java
Consider case below:
S = eefe
^
with A = e and B = eef
You can't take the first e with A, because resulting substring would then be efe
and B can't match efe.
So in case of ambiguity you have to explore the two condition: should A take or should B take ?
the recursion would be:
// return true if A and B can match S, false otherwise
bool AOrB(s, iS, iA, iB) {
if (iS > s.length) {
// we consumed all chars in S: SUCCESS
return true
}
a = A[iA]
b = B[iB]
s = S[iS]
// consider all possibilities...
if (a == s) {
if (b == s) {
// we need to explore both cases
return AOrB(s, iS+1, iA+1, iB) || AOrB(s, iS+1, iA, iB+1)
} else {
// only A is candidate!
return AOrB(s, iS+1, iA+1, iB)
}
} else {
if (b == s) {
// only B is candidate
return AOrB(s, iS+1, iA, iB+1)
} else {
// no one matches s
return false
}
}
}
This can be simplified as
AOrB(s, iS, iA, iB) {
if (iS > s.length) {
return true
}
a = A[iA]
b = B[iB]
s = S[iS]
// consider all possibilities...
bool hasSolution = false
if (a == s) {
hasSolution ||= AOrB(s, iS+1, iA+1, iB)
}
if (b == s) {
hasSolution ||= AOrB(s, iS+1, iA, iB+1)
}
return hasSolution
}
which is equivalent to
AOrB(s, iS, iA, iB) {
if (iS > s.length) {
return true
}
a = A[iA]
b = B[iB]
s = S[iS]
return a == s && AOrB(s, iS+1, iA+1, iB) || b == s && AOrB(s, iS+1, iA, iB+1)
}
Finally, you may take the dynamic approach route:
You build the candidates starting from S[0] (so 0 candidates if nor A or B matches S[0], 1 if only A or B matches, or 2 candidates if both match)
Then you use each of those candidates as starting point for s[1], and so forth
dpAOrB (S) {
// a candidate is a couple { iA, iB } where iA is the next char to be matched by A
// initially you only have one candidate: the couple { iA: 0, iB: 0 }
candidates = new Set({ iA: 0, iB: 0 })
foreach(s of S) {
nextCandidates = new Set()
foreach (candidate of candidates) {
if(A[candidate.iA] == s) {
nextCandidates.push({
iA: iA + 1, // A can take, that's a candidate
iB: candidate.iB
})
}
if(B[candidate.iB] == s) {
nextCandidates.push({
iA: iA,
iB: candidate.iB + 1
})
}
}
// if we could not generate any candidate, we can't match S
if (nextCandidates.empty () {
return false
}
candidates = nextCandidates
}
// we consumed all chars of S!
return true
}
Below just some demo just to show "it works"
function dpAOrB (S, A, B) {
let candidates = [{ iA: 0, iB: 0 }]
return S.split('').every(s => {
const nextCandidates = []
candidates.forEach(({ iA, iB }) => {
A[iA] === s && nextCandidates.push({ iA: iA + 1, iB })
B[iB] === s && nextCandidates.push({ iA, iB: iB + 1 })
})
candidates = nextCandidates
return nextCandidates.length
})
}
console.log(dpAOrB('Can we merge it? Yes, we can!', 'nwe me?s, e cn', 'Ca erg it Yewa!'))
console.log(dpAOrB("codewars", "code", "wars"))
console.log(dpAOrB("codewars", "cdwr", "oeas"))
console.log(dpAOrB("codewars", "cod", "wars"))
console.log(dpAOrB("a ttest", "a tt", "tes")) // thx Turo
Improvement: no dupplication
Lastly, as exhibed by Turo's code
We can notice that we can have dupplicate candidates.
Consider S = 'aaaabc', A='aab', B='aac'.
After having consumed 'aa':
candidates [
{ iA: 2, iB: 0 },
{ iA: 1, iB: 1 },
{ iA: 1, iB: 1 },
{ iA: 0, iB: 2 }
]
Here we took in order AA, AB, BA, BB. However AB and BA both lead to the candidate { iA: 1, iB: 1 }
So we can shrink the space state we explore by considering the hash key iA+''+iB and avoid dupplicates.
function dpAOrB (S, A, B) {
let candidates = new Map([[0+'_'+0, { iA: 0, iB: 0 }]])
return S.split('').every(s => {
const nextCandidates = new Map()
;[...candidates].forEach(([_, { iA, iB }]) => {
A[iA] === s && nextCandidates.set([iA+1, iB].join('_'), { iA: iA + 1, iB })
B[iB] === s && nextCandidates.set([iA, iB+1].join('_'), { iA, iB: iB + 1 })
})
candidates = nextCandidates
// notice only one { iA: 1, iB: 1 }
console.log('candidates', [...candidates.values()])
return nextCandidates.size
})
}
console.log(dpAOrB("aaaa", "aab", "aac"))
You forgot some returns at the recursive isMerge-calls, so you end up in the return false at the bottom.
if (isMerge(...)) {
return true;
}
EDIT:
forgot to check the other way if the first one fails
And, for the fun of it, here a classical(maybe historic already) approach to do this without recursion(if there could bey cycles in your states you'd need a Set<State> closed to check for it):
public class StringMerger2 {
private class State {
String current;
String left;
String right;
public State(String current, String left, String right) {
super();
this.current = current;
this.left = left;
this.right = right;
}
}
private Queue<State> open = new LinkedList<>();
private String target;
public StringMerger2(String target, String part1, String part2) {
super();
this.target = target;
open.add(new State("", part1, part2));
}
public boolean isMerge() {
while (!open.isEmpty()) {
State state = open.poll();
System.out.println(state.current + ":" + state.left + ":" + state.right);
if (state.current.equals(target)) {
return true;
}
int pos = state.current.length();
if (pos == target.length()) { // for safety reasons, one should never end here
return false;
}
if (state.left.startsWith(target.substring(pos, pos + 1))) {
open.add(new State(state.current + state.left.charAt(0), state.left.substring(1), state.right));
}
if (state.right.startsWith(target.substring(pos, pos + 1))) {
open.add(new State(state.current + state.right.charAt(0), state.left, state.right.substring(1)));
}
}
return false;
}
public static void main(String[] args) {
System.out.println(new StringMerger2("a ttest", "a tt", "tes").isMerge());
System.out.println(
new StringMerger2("Can we merge it? Yes, we can!", "nwe me?s, e cn", "Ca erg it Yewa!").isMerge());
System.out.println(new StringMerger2("codewars", "code", "wars").isMerge());
System.out.println(new StringMerger2("codewars", "cdwr", "oeas").isMerge());
System.out.println(new StringMerger2("codewars", "cod", "wars").isMerge());
System.out.println(new StringMerger2("a ttest", "a tt", "tes").isMerge());
System.out.println(new StringMerger2("a ttest", " tta", "tes").isMerge());
}
}
Your code is way too complex. Here's a way to do it:
public class StringMerger
{
static boolean isMerge(String s, String part1, String part2)
{
int len1 = part1.length();
int len2 = part2.length();
int i1 = 0;
int i2 = 0;
for(char c : s.toCharArray())
{
if(i1<len1 && c==part1.charAt(i1))
i1++;
else if(i2<len2 && c==part2.charAt(i2))
i2++;
else
return false;
}
return i1==len1 && i2==len2;
}
public static void main(String []args)
{
System.out.println(isMerge("codewars", "cdw", "oears"));
}
}
EDIT: as Turo pointed out, it works only under the assumption that part1 and part2 don't share any letters.
Based on #Olivier answer, and without looking at it while redoing the codewars exercise, we could also write:
public class StringMerger {
public static boolean isMerge/*ππ*/(String s, String part1, String part2) {
System.out.println("\n\ns: "+s);
System.out.println("part1: "+part1);
System.out.println("part2: "+part2);
if(s.isEmpty() && part1.isEmpty() && part2.isEmpty()) return true;
if(part1.equals(part2)) return false;
int pointer1 = 0, pointer2 = 0;
for(char letter : s.toCharArray()){
if(pointer1 < part1.length() && part1.charAt(pointer1)==letter){
pointer1++;
}
if(pointer2 < part2.length() && part2.charAt(pointer2)==letter){
pointer2++;
}
}
System.out.println("pointer1: "+pointer1);
System.out.println("pointer2: "+pointer2);
return s.length()==pointer1+pointer2 && pointer1==part1.length() && pointer2==part2.length();
}
}
Where we just count the letters in the original string s, which can be found either in part1 or part2, and then we check if that count is equal to the length of s.
A trace could be:
s: codewars
part1: code
part2: code
s: More progress
part1: More ess
part2: pro
pointer1: 8
pointer2: 3
inspired by Olivier's answer, tests from grodzi and Turo
(does not respect order)
public class StringMerger {
final static class MergePart {
private String str;
public MergePart(String str) {
this.str = str;
}
private void removeCharFromStr(int index) {
str = new StringBuilder(str).deleteCharAt(index).toString();
}
public boolean isComplete() {
return str.length() == 0;
}
public boolean compare(char c) {
if (isComplete()) return false;
int index = str.indexOf(c);
if (index < 0) return false;
removeCharFromStr(index);
return true;
}
}
static boolean isMerge(String s, String part1, String part2) {
MergePart m1 = new MergePart(part1);
MergePart m2 = new MergePart(part2);
for(char c : s.toCharArray())
{
if (m1.compare(c)) continue;
if (m2.compare(c)) continue;
return false;
}
return m1.isComplete() && m2.isComplete();
}
public static void main(String []args) {
System.out.println(isMerge("Can we merge it? Yes, we can!", "nwe me?s, e cn", "Ca erg it Yewa!")); // true
System.out.println(isMerge("codewars", "code", "wars")); // true
System.out.println(isMerge("codewars", "cdwr", "oeas")); // true
System.out.println(isMerge("codewars", "cod", "wars")); // false
System.out.println(isMerge("a ttest", "a tt", "tes")); // true
}
}
This is tricky when part1 and part2 both have same characters at certain index. We can't guarantee which one would match later. So, this is like a binary tree where we have 2 options at each stage in case of clash.
Only way to find out is to explore both options. So you create a queue which holds an integer array of size 2. First index moves part1's pointer and second index moves part2's pointer in case of a match. If we reach a stage where both have reached their lengths completely and if current iteration character in String is also last, we return true, else we return false.
Note that there can also be instances where the current character in iteration didn't match anyone from the queue. In that case, we return false as well since we are looking for a complete match. This is taken care in the below code by the variable took.
Snippet:
import java.util.*;
public class StringMerger {
public static boolean isMerge(String s, String part1, String part2) {
if(s.length() == 0){
if(part1.length() == 0 && part2.length() == 0) return true;
return false;
}
Queue<int[]> q = new LinkedList<int[]>();
q.offer(new int[]{0,0});
for(int i=0;i<s.length();++i){
int size = q.size();
boolean took = false;
for(int j=0;j<size;++j){
int[] t = q.poll();
if(t[0] < part1.length() && s.charAt(i) == part1.charAt(t[0])){
if(t[0] + 1 == part1.length() && t[1] == part2.length() && i == s.length() - 1) return true;
took = true;
q.offer(new int[]{t[0] + 1,t[1]});
}
if(t[1] < part2.length() && s.charAt(i) == part2.charAt(t[1])){
if(t[1] + 1 == part2.length() && t[0] == part1.length() && i == s.length() - 1) return true;
took = true;
q.offer(new int[]{t[0],t[1] + 1});
}
}
if(took == false) return false;
}
return false;
}
}
I am reading "Head First Java" book and I came across the problem in chapter 5 with the battleship game (with simple version). I knew that the book's code doesn't work and I tried my self fixing it, but it still didn't work.
So tried to google it and I found some post on this website but I still have a problem. The game isn't working properly as it should.
If a player enters any random number, the output is always "hit"...
This is the last version of the code:
DotCom class:
public class DotCom {
private ArrayList<String> locationCells = new ArrayList<>();
public void setlocationCells(int[] loc) {
if (loc != null)
for (int val : loc)
locationCells.add(String.valueOf(val));
}
public String checkYourself(String userInput) {
String result = "miss";
int index = locationCells.indexOf(userInput);
if (index >= 0) {
locationCells.remove(index);
}
if (locationCells.isEmpty()) {
result = "kill";
} else {
result = "hit";
}
System.out.println(result);
return result;
}
}
DotComGame class:
public class DotComGame {
public static void main(String[] args) {
int guessingTimes = 0;
DotCom dot = new DotCom();
GameHelperrr helper = new GameHelperrr();
int randomNum = (int) (Math.random() * 5);
int[] locations = { randomNum, randomNum + 1, randomNum + 2 };
dot.setlocationCells(locations);
boolean isAlive = true;
while (isAlive == true) {
String guess = helper.getUserInput("Enter a number");
String result = dot.checkYourself(guess);
guessingTimes++;
if (result.equals("kill")) {
isAlive = false;
System.out.println("You took " + guessingTimes + " guesses");
}
}
}
}
I would really appreciate to get a detailed and understandable answer, because I'm stuck and I couldn't move on with the book for a few days now.
int index = locationCells.indexOf(userInput);
This method will return -1 if the element doesn't exist in the collection.
So if you miss, it won't hit this condition:
if (index >= 0) {
locationCells.remove(index);
}
There are still elements in this collection because you didn't remove anything...
if (locationCells.isEmpty()) {
result = "kill";
} else {
result = "hit";
}
So on a miss, the result still shows "hit."
Try this instead:
if (locationCells.isEmpty()) {
result = "kill";
} else {
result = index == -1 ? "miss" : "hit";
}
If you haven't killed the opponents ships, then you either miss all ships or you hit a single ship.
I would guess the checkYourself-Method must be like this:
public String checkYourself(String userInput) {
String result = "miss";
int index = locationCells.indexOf(userInput);
if(index >= 0) {
locationCells.remove(index);
if (locationCells.isEmpty()) {
result = "kill";
}else {
result = "hit";
}
}
System.out.println(result);
return result;
}
In it's current form the ArrayList is never empty because you insert 3 Values but only remove 1 if the user-input is in the list so .isEmpty() is never TRUE.
I want to write a function that return true if if and only if
String a is always followed by String b, and String b is always followed by string c, this is what I wrote but it doesn't work :
public static boolean follows2(String s, String a, String b, String c) {
boolean res = true;
for (int i = 0; i < s.length(); i++) {
if (charAtPos(s, i).equals(a)) {
if (!(charAtPos(s, i + 1).equals(b))) {
res = false;
if (!(charAtPos(s, i + 2).equals(c))) {
res = false;
}
}
}
}
return res;
}
public static void main(String[] args) {
System.out.println(follows2(" koali oliali ", "a", "l", "i"));
// RETURN TRUE OK since for each " a" is followed by "l" then "i"
System.out.println(follows2("ipoipa", "i", "p", "a"));
//RETURN TRUE BUT IT'S NOT !
// The first " i" is followed by " p" then "o" which is not good
}
Here is the function that I wrote for:
String a is always followed by String b ( It works )
public static boolean follows(String s, String a, String b) {
boolean res = true;
for (int i = 0; i < s.length(); i++) {
if (charAtPos(s, i).equals(a)) {
if (!(charAtPos(s, i + 1).equals(b))) {
res = false;
}
}
}
return res;
}
public static String charAtPos(String s, int i) {
return String.valueOf(s.charAt(i));
}
public static void main(String[] args) {
System.out.println(follows("Arnold Barney", "r", "n"));
// RETURN TRUE because each "r" are followed by the letter "n"
System.out.println(follows("Arnold Barney", "n", "o"));
// RETURN FALSE , because not every "n" is followed by "o"
}
What can be done in my first program to make it work ?
Thank you
With recursion:
public static boolean follows(String s, String a, String b, String c) {
int ai = s.indexOf(a);
if (ai == -1) {
return true; // No more 'a' string, we're all good
}
int bi = s.indexOf(a + b);
int ci = s.indexOf(a + b + c);
if (bi != ai || ci != ai) {
return false; // Strings 'b' and 'bc' don't follow 'a', so the check failed
}
return follows(s.substring(ai + a.length()), a, b, c);
}
In reality, bi could be removed.
The problem you have is that you are not actually entering the nested if statements due to a minor flaw in your logic.
I would change it to this which checks whether i + 1 is equal to String b || i + 2 is equal to String c
public static boolean follows2(String s, String a, String b, String c) {
boolean res = true;
for (int i = 0; i < s.length(); i++) {
if (charAtPos(s, i).equals(a)) {
if (!(charAtPos(s, i + 1).equals(b)) || !(charAtPos(s, i + 2).equals(c))) {
res = false;
}
}
}
return res;
}
Because the code doesn't do what you think it does.
if you'll add some prints to the code, you'll see you never set "res" to false.
Let's debug the case you're testing:
When meet the the first letter - "i" - it entered the first if.
the next letter is "p" so you're not entered the 2nd if, so you'll continue to the next letter in the "for".
Here's my attempt (haven't tested it)
boolean hasOnlyCompleteSequences(String source, char.. chars) {
for (int s = 0; s < source.length(); s++) {
int c = 0;
if (source.charAt(s) == chars[c]) {
if (!isCompleteSequence(source, s + 1, chars, c + 1)) {
return false;
}
}
}
return true;
}
boolean isCompleteSequence(String source, int s, char[] chars, int c) {
while (s < source.length() && c < chars.length) {
// note: the indices get increased AFTER comparison
if (source.charAt(s++) != chars[c++]) {
return false;
}
}
// cover the case that the source String ends with an incomplete sequence
if (s == source.length() && c < chars.length) {
return false;
}
return true;
}
This is a much cleaner answer (tested & working great):
public static boolean follows(String s, String...strings) {
int number = 0;
for(int i = 0; i<s.length(); i++){
if(strings[number].length()+i<s.length() && s.substring(i, strings[number].length()+i).equals(strings[number]) && number+1 != strings.length) number++;
else if(number!=0 && !s.substring(i, strings[number].length()+i).equals(strings[number])) return false;
else number = 0;
}
return true;
}
It fixes many problems with your code:
Working with strings but using "charAt"
Copying the function just to add a parameter
Given a string and a non-empty substring sub, compute recursively the largest substring which starts and ends with sub and return its length.
strDist("catcowcat", "cat") β 9
strDist("catcowcat", "cow") β 3
strDist("cccatcowcatxx", "cat") β 9
my solution
public int strDist(String str, String sub) {
int i = sub.length();
int j = str.length();
int count = 0;
if (str.length() == 1 && str.equals(sub)) {
return 1;
} else if (str.length() < sub.length() || str.length() <= 1) {
return 0;
}
if (str.substring(0, i).equals(sub)) {
if (str.substring(str.length() - i, str.length()).equals(sub)) {
return str.length();
} else {
strDist(str.substring(0, str.length() - i), sub);
}
} else {
strDist(str.substring(1, str.length()), sub);
}
return 0;
}
tell me how to correct my code?
Why does this need to be done with recursion?
Edit: fixed code to handle case where sub is not present in str, or only present once.
public int strDist(String str, String sub) {
int last=str.lastIndexOf(sub);
if (last != -1) {
int first=str.indexOf(sub);
if (first != last)
return last - first + sub.length();
}
}
return 0;
}
Recursion is great, if it is suited to the problem. In this case, recursion doesn't add value, and writing it with recursion for the sake of recursion makes the code inefficient.
This will , "compute recursively the largest substring which starts and ends with sub and return its length" as you described.
public class PuzzlingRecursion {
static String substringFound = "";
public static void main(String[] args) {
String sentence = "catcowcat";
String substring = "cat";
int sizeString = findNumberOfStrings(sentence, substring, 0);
System.out.println("you are searching for: " + substring);
System.out.println("in: " + sentence);
System.out.println("substring which starts and ends with sub and return its length is:"+substringFound + ", " + sizeString);
}
private static int findNumberOfStrings(String subStringPassed,
String setenecePassed, int count) {
if (subStringPassed.length() == 0) {
return count + 0;
}
if (subStringPassed.length() < setenecePassed.length()) {
return count + 0;
}
count++;
String lastStringMiddle = subStringPassed.replaceAll("(.*?)" + "("
+ setenecePassed + ")" + "(.*?)" + "(" + setenecePassed + ")"
+ "(.*?.*)", "$3");
if (subStringPassed.contains(setenecePassed)
&& lastStringMiddle.length() != setenecePassed.length()) {
if (subStringPassed.contains(setenecePassed)
&& lastStringMiddle.contains(setenecePassed)) {
// only found one item no pattern but according to the example
// you posted it should return the length of one word/substring
count = setenecePassed.length();
substringFound = subStringPassed;
return count;
}
}
// makesure the lastSrtringMiddle has the key we are search
if (!lastStringMiddle.equals(subStringPassed)) {
subStringPassed = subStringPassed.replaceFirst(setenecePassed, "");
String lastString = subStringPassed.substring(0,
subStringPassed.lastIndexOf(setenecePassed));
if (null != lastString && !"".equals(lastString)) {
count = lastStringMiddle.length() + setenecePassed.length()
+ setenecePassed.length();
substringFound = setenecePassed + lastStringMiddle
+ setenecePassed;
subStringPassed = "";
}
return findNumberOfStrings(subStringPassed, setenecePassed, count);
}
return count;
}
}
I think this is much nicer recursive solution:
public int strDist(String str, String sub) {
if (str.length()==0) return 0;
if (!str.startsWith(sub))
return strDist(str.substring(1),sub);
if (!str.endsWith(sub))
return strDist(str.substring(0,str.length()-1),sub);
return str.length();
}
I am trying to create a Palindrome program using recursion within Java but I am stuck, this is what I have so far:
public static void main (String[] args){
System.out.println(isPalindrome("noon"));
System.out.println(isPalindrome("Madam I'm Adam"));
System.out.println(isPalindrome("A man, a plan, a canal, Panama"));
System.out.println(isPalindrome("A Toyota"));
System.out.println(isPalindrome("Not a Palindrome"));
System.out.println(isPalindrome("asdfghfdsa"));
}
public static boolean isPalindrome(String in){
if(in.equals(" ") || in.length() == 1 ) return true;
in= in.toUpperCase();
if(Character.isLetter(in.charAt(0))
}
public static boolean isPalindromeHelper(String in){
if(in.equals("") || in.length()==1){
return true;
}
}
}
Can anyone supply a solution to my problem?
Here I am pasting code for you:
But, I would strongly suggest you to know how it works,
from your question , you are totally unreadable.
Try understanding this code. Read the comments from code
import java.util.Scanner;
public class Palindromes
{
public static boolean isPal(String s)
{
if(s.length() == 0 || s.length() == 1)
// if length =0 OR 1 then it is
return true;
if(s.charAt(0) == s.charAt(s.length()-1))
// check for first and last char of String:
// if they are same then do the same thing for a substring
// with first and last char removed. and carry on this
// until you string completes or condition fails
return isPal(s.substring(1, s.length()-1));
// if its not the case than string is not.
return false;
}
public static void main(String[]args)
{
Scanner sc = new Scanner(System.in);
System.out.println("type a word to check if its a palindrome or not");
String x = sc.nextLine();
if(isPal(x))
System.out.println(x + " is a palindrome");
else
System.out.println(x + " is not a palindrome");
}
}
Well:
It's not clear why you've got two methods with the same signature. What are they meant to accomplish?
In the first method, why are you testing for testing for a single space or any single character?
You might want to consider generalizing your termination condition to "if the length is less than two"
Consider how you want to recurse. One option:
Check that the first letter is equal to the last letter. If not, return false
Now take a substring to effectively remove the first and last letters, and recurse
Is this meant to be an exercise in recursion? That's certainly one way of doing it, but it's far from the only way.
I'm not going to spell it out any more clearly than that for the moment, because I suspect this is homework - indeed some may consider the help above as too much (I'm certainly slightly hesitant myself). If you have any problems with the above hints, update your question to show how far you've got.
public static boolean isPalindrome(String in){
if(in.equals(" ") || in.length() < 2 ) return true;
if(in.charAt(0).equalsIgnoreCase(in.charAt(in.length-1))
return isPalindrome(in.substring(1,in.length-2));
else
return false;
}
Maybe you need something like this. Not tested, I'm not sure about string indexes, but it's a start point.
I think, recursion isn't the best way to solve this problem, but one recursive way I see here is shown below:
String str = prepareString(originalString); //make upper case, remove some characters
isPalindrome(str);
public boolean isPalindrome(String str) {
return str.length() == 1 || isPalindrome(str, 0);
}
private boolean isPalindrome(String str, int i) {
if (i > str.length / 2) {
return true;
}
if (!str.charAt(i).equals(str.charAt(str.length() - 1 - i))) {
return false;
}
return isPalindrome(str, i+1);
}
Here is my go at it:
public class Test {
public static boolean isPalindrome(String s) {
return s.length() <= 1 ||
(s.charAt(0) == s.charAt(s.length() - 1) &&
isPalindrome(s.substring(1, s.length() - 1)));
}
public static boolean isPalindromeForgiving(String s) {
return isPalindrome(s.toLowerCase().replaceAll("[\\s\\pP]", ""));
}
public static void main(String[] args) {
// True (odd length)
System.out.println(isPalindrome("asdfghgfdsa"));
// True (even length)
System.out.println(isPalindrome("asdfggfdsa"));
// False
System.out.println(isPalindrome("not palindrome"));
// True (but very forgiving :)
System.out.println(isPalindromeForgiving("madam I'm Adam"));
}
}
public class palin
{
static boolean isPalin(String s, int i, int j)
{
boolean b=true;
if(s.charAt(i)==s.charAt(j))
{
if(i<=j)
isPalin(s,(i+1),(j-1));
}
else
{
b=false;
}
return b;
}
public static void main()
{
String s1="madam";
if(isPalin(s1, 0, s1.length()-1)==true)
System.out.println(s1+" is palindrome");
else
System.out.println(s1+" is not palindrome");
}
}
Some of the codes are string heavy. Instead of creating substring which creates new object, we can just pass on indexes in recursive calls like below:
private static boolean isPalindrome(String str, int left, int right) {
if(left >= right) {
return true;
}
else {
if(str.charAt(left) == str.charAt(right)) {
return isPalindrome(str, ++left, --right);
}
else {
return false;
}
}
}
public static void main(String []args){
String str = "abcdcbb";
System.out.println(isPalindrome(str, 0, str.length()-1));
}
Here are three simple implementations, first the oneliner:
public static boolean oneLinerPalin(String str){
return str.equals(new StringBuffer(str).reverse().toString());
}
This is ofcourse quite slow since it creates a stringbuffer and reverses it, and the whole string is always checked nomatter if it is a palindrome or not, so here is an implementation that only checks the required amount of chars and does it in place, so no extra stringBuffers:
public static boolean isPalindrome(String str){
if(str.isEmpty()) return true;
int last = str.length() - 1;
for(int i = 0; i <= last / 2;i++)
if(str.charAt(i) != str.charAt(last - i))
return false;
return true;
}
And recursively:
public static boolean recursivePalin(String str){
return check(str, 0, str.length() - 1);
}
private static boolean check (String str,int start,int stop){
return stop - start < 2 ||
str.charAt(start) == str.charAt(stop) &&
check(str, start + 1, stop - 1);
}
public static boolean isPalindrome(String str)
{
int len = str.length();
int i, j;
j = len - 1;
for (i = 0; i <= (len - 1)/2; i++)
{
if (str.charAt(i) != str.charAt(j))
return false;
j--;
}
return true;
}
Try this:
package javaapplicationtest;
public class Main {
public static void main(String[] args) {
String source = "mango";
boolean isPalindrome = true;
//looping through the string and checking char by char from reverse
for(int loop = 0; loop < source.length(); loop++){
if( source.charAt(loop) != source.charAt(source.length()-loop-1)){
isPalindrome = false;
break;
}
}
if(isPalindrome == false){
System.out.println("Not a palindrome");
}
else
System.out.println("Pailndrome");
}
}
String source = "liril";
StringBuffer sb = new StringBuffer(source);
String r = sb.reverse().toString();
if (source.equals(r)) {
System.out.println("Palindrome ...");
} else {
System.out.println("Not a palindrome...");
}
public class chkPalindrome{
public static String isPalindrome(String pal){
if(pal.length() == 1){
return pal;
}
else{
String tmp= "";
tmp = tmp + pal.charAt(pal.length()-1)+isPalindrome(pal.substring(0,pal.length()-1));
return tmp;
}
}
public static void main(String []args){
chkPalindrome hwObj = new chkPalindrome();
String palind = "MADAM";
String retVal= hwObj.isPalindrome(palind);
if(retVal.equals(palind))
System.out.println(palind+" is Palindrome");
else
System.out.println(palind+" is Not Palindrome");
}
}
Here is a recursive method that will ignore specified characters:
public static boolean isPal(String rest, String ignore) {
int rLen = rest.length();
if (rLen < 2)
return true;
char first = rest.charAt(0)
char last = rest.charAt(rLen-1);
boolean skip = ignore.indexOf(first) != -1 || ignore.indexOf(last) != -1;
return skip || first == last && isPal(rest.substring(1, rLen-1), ignore);
}
Use it like this:
isPal("Madam I'm Adam".toLowerCase(), " ,'");
isPal("A man, a plan, a canal, Panama".toLowerCase(), " ,'");
It does not make sense to include case insensitivity in the recursive method since it only needs to be done once, unless you are not allowed to use the .toLowerCase() method.
there's no code smaller than this:
public static boolean palindrome(String x){
return (x.charAt(0) == x.charAt(x.length()-1)) &&
(x.length()<4 || palindrome(x.substring(1, x.length()-1)));
}
if you want to check something:
public static boolean palindrome(String x){
if(x==null || x.length()==0){
throw new IllegalArgumentException("Not a valid string.");
}
return (x.charAt(0) == x.charAt(x.length()-1)) &&
(x.length()<4 || palindrome(x.substring(1, x.length()-1)));
}
LOL B-]
public static boolean isPalindrome(String p)
{
if(p.length() == 0 || p.length() == 1)
// if length =0 OR 1 then it is
return true;
if(p.substring(0,1).equalsIgnoreCase(p.substring(p.length()-1)))
return isPalindrome(p.substring(1, p.length()-1));
return false;
}
This solution is not case sensitive. Hence, for example, if you have the following word : "adinida", then you will get true if you do "Adninida" or "adninida" or "adinidA", which is what we want.
I like #JigarJoshi answer, but the only problem with his approach is that it will give you false for words which contains caps.
Palindrome example:
static boolean isPalindrome(String sentence) {
/*If the length of the string is 0 or 1(no more string to check),
*return true, as the base case. Then compare to see if the first
*and last letters are equal, by cutting off the first and last
*letters each time the function is recursively called.*/
int length = sentence.length();
if (length >= 1)
return true;
else {
char first = Character.toLowerCase(sentence.charAt(0));
char last = Character.toLowerCase(sentence.charAt(length-1));
if (Character.isLetter(first) && Character.isLetter(last)) {
if (first == last) {
String shorter = sentence.substring(1, length-1);
return isPalindrome(shorter);
} else {
return false;
}
} else if (!Character.isLetter(last)) {
String shorter = sentence.substring(0, length-1);
return isPalindrome(shorter);
} else {
String shorter = sentence.substring(1);
return isPalindrome(shorter);
}
}
}
Called by:
System.out.println(r.isPalindrome("Madam, I'm Adam"));
Will print true if palindrome, will print false if not.
If the length of the string is 0 or 1(no more string to check), return true, as the base case. This base case will be referred to by function call right before this. Then compare to see if the first and last letters are equal, by cutting off the first and last letters each time the function is recursively called.
Here is the code for palindrome check without creating many strings
public static boolean isPalindrome(String str){
return isPalindrome(str,0,str.length()-1);
}
public static boolean isPalindrome(String str, int start, int end){
if(start >= end)
return true;
else
return (str.charAt(start) == str.charAt(end)) && isPalindrome(str, start+1, end-1);
}
public class PlaindromeNumbers {
int func1(int n)
{
if(n==1)
return 1;
return n*func1(n-1);
}
static boolean check=false;
int func(int no)
{
String a=""+no;
String reverse = new StringBuffer(a).reverse().toString();
if(a.equals(reverse))
{
if(!a.contains("0"))
{
System.out.println("hey");
check=true;
return Integer.parseInt(a);
}
}
// else
// {
func(no++);
if(check==true)
{
return 0;
}
return 0;
}
public static void main(String[] args) {
// TODO code application logic here
Scanner in=new Scanner(System.in);
System.out.println("Enter testcase");
int testcase=in.nextInt();
while(testcase>0)
{
int a=in.nextInt();
PlaindromeNumbers obj=new PlaindromeNumbers();
System.out.println(obj.func(a));
testcase--;
}
}
}
/**
* Function to check a String is palindrome or not
* #param s input String
* #return true if Palindrome
*/
public boolean checkPalindrome(String s) {
if (s.length() == 1 || s.isEmpty())
return true;
boolean palindrome = checkPalindrome(s.substring(1, s.length() - 1));
return palindrome && s.charAt(0) == s.charAt(s.length() - 1);
}
Simple Solution
2 Scenario --(Odd or Even length String)
Base condition& Algo recursive(ch, i, j)
i==j //even len
if i< j recurve call (ch, i +1,j-1)
else return ch[i] ==ch[j]// Extra base condition for old length
public class HelloWorld {
static boolean ispalindrome(char ch[], int i, int j) {
if (i == j) return true;
if (i < j) {
if (ch[i] != ch[j])
return false;
else
return ispalindrome(ch, i + 1, j - 1);
}
if (ch[i] != ch[j])
return false;
else
return true;
}
public static void main(String[] args) {
System.out.println(ispalindrome("jatin".toCharArray(), 0, 4));
System.out.println(ispalindrome("nitin".toCharArray(), 0, 4));
System.out.println(ispalindrome("jatinn".toCharArray(), 0, 5));
System.out.println(ispalindrome("nittin".toCharArray(), 0, 5));
}
}
for you to achieve that, you not only need to know how recursion works but you also need to understand the String method.
here is a sample code that I used to achieve it: -
class PalindromeRecursive {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("Enter a string");
String input=sc.next();
System.out.println("is "+ input + "a palindrome : " + isPalindrome(input));
}
public static boolean isPalindrome(String s)
{
int low=0;
int high=s.length()-1;
while(low<high)
{
if(s.charAt(low)!=s.charAt(high))
return false;
isPalindrome(s.substring(low++,high--));
}
return true;
}
}