recursion subtraction problem from CodingBat.com - java

I'm trying to solve this question from CodingBat.com. - https://codingbat.com/prob/p143900
I run the recursion twice, then I subtract their values to give the final answer. Individually, I and J are getting the correct values, but when I do i - j, the result doesn't make sense.
public int countHi2(String str) {
if(str.length() == 0)
return 0;
int i = count(str, "hi");
int j = count(str, "xhi");
return i-j;
}
public int count(String str, String match)
{
int i = str.indexOf(match);
if(i != -1)
return 1 + countHi2(str.substring(i+match.length()-1));
else
return 0 + countHi2(str.substring(1));
}

I think you treat rescursive illegally. You should not use external method or even split a string.
public int countHi2(String str) {
if (str == null || str.length() < 2)
return 0;
if (str.startsWith("hi"))
return 1 + countHi2(str.substring(2));
return countHi2(str.substring(str.startsWith("xhi") ? 3 : 1));
}

you might wanna use:
str.substring(i+match.length())
suppose your match was on index 3 and the length of the word to match is 2, then you want the substring from index 5 to the end.
thus,
str.substring(5);
which includes the index 5
though, i don't understand why you used
return 0 + countHi2(str.substring(1));
because this condition will hold if no match is found, so what is the point of checking for a match again in a substring !!!

Related

How do I manually find a substring in a string? (Java)

public int lookFor(String s) {
final int EXIST = 1;
final int NOT_EXIST = -1;
int thisIndex = 0;
int otherIndex = 0;
char thisNext;
char otherNext;
if (s == null || s.length() == 0)
return NOT_EXIST;
for(; thisIndex < this.mainString.length() ; ) {
thisNext = this.mainString.charAt(thisIndex);
otherNext = s.charAt(otherIndex);
if (thisNext == otherNext) {
thisIndex++;
otherIndex++;
}
else if (thisNext != otherNext)
thisIndex++;
if (otherIndex == s.length()-1)
return EXIST;
}
return NOT_EXIST;
}
This is my attempt so far.
mainString = the main string I want to find the substring in.
s = the substring.
So my idea was to get the first chars of both strings, see if they equal. if they don't, i'll get the second char of mainString, see if they equal (mainString second char to s first char). If they're not equal, i'll get the third char of mainString and so forth. Once they're equal, i'll get the next char of both strings and see if they both equal.
Basically the loops knows that mainString contains s when index of s equals to s length minus one (that means the loop looped all the way to the last char inc, of s, so s index == s length -1).
Is the logic I'm trying to work with incorrect? or I just executed it not good? i'll happy to get answers!
Here's my naïve approach:
private final int EXIST = 1;
private final int NOT_EXIST = -1;
private int lookFor(String a, String b, int index) {
for (int i = 0; i < b.length(); i++) {
if ((index + i) >= a.length()) return NOT_EXIST;
if (a.charAt(index + i) != b.charAt(i)) return NOT_EXIST;
}
return EXIST;
}
public int lookFor(String a, String b) {
char start = b.charAt(0);
for (int i=0; i < a.length(); i++) {
if (a.charAt(i) == start) {
if (lookFor(a, b, i) == EXIST) return EXIST;
}
}
return NOT_EXIST;
}
Though, I'm not sure why you would do this when you could just do:
int ret = a.contains(b) ? EXIST : NOT_EXIST
However I wanted to actually answer your question.
Here's a slightly improved version that satisfies your "all in one method" requirement.
public static int lookFor(String a, String b) {
// Fancy way of preventing errors when one of the strings is empty
boolean az = a.length() == 0;
boolean bz = b.length() == 0;
if (az ^ bz) return NOT_EXIST;
// Need this next line if you want to interpret two empty strings as containing eachother
if (az && bz) return EXIST;
char start = b.charAt(0);
// This is known as a "label". Some say it's bad practice.
outer:
for (int i=0; i < a.length(); i++) {
if (a.charAt(i) == start) {
// Instead of using two methods, we can condense it like so
for (int q = 0; q < b.length(); q++) {
if ((i + q) >= a.length()) continue outer;
if (a.charAt(i + q) != b.charAt(q)) continue outer;
}
return EXIST;
}
}
return NOT_EXIST;
}
To find a substring "by hand", you need a nested loop; i.e. a loop inside a loop.
The outer loop tries all of the possible start positions for the substring in the string.
For a given start position, the inner loop tests all of the characters of the string that you are looking for against the string you are searching in.
In the naive substring search algorithm, the outer loop steps starts at index zero, and increments the index by one until it gets to the end of the string being searched. This can be improved on:
Every non-null string "contains" the empty string. It may be worth treating this as a special case.
It is easy to see that the outer loop can usually stop before the final. If you are searching for a string of length (say) 3, then the outer loop can stop at 3 from the end. (Think about it ....)
There are some clever algorithms which allow the outer loop to skip over some indexes. If you are interested, start by Googling for "Boyer-Moore string search".
(Note: the looping could be replaced with / written using recursion, but it is still there.)
Your code doesn't have a nested loops. By my reading, it is only going to find a match if the string you are searching for is at the start of the string you are searching. That is not correct.

How would I search a string or the first single occurrence a sub-string?

For example, searching in the string "aabacbba"for the substring"a"would return the index of3`:
private int findString(String str, int start) {
// TODO: Implement string search
}
I'm not sure how to check to make sure the string is alone and repeating.
Here are some things to help you:
Strings have a s.charAt(i); method that returns a character at the given index.
A String also has a length() method that returns the amount of character it has.
You can use a for loop to check each character and see if it matches the one you want.
for(int i = 0; i < line.length(); i++)
I believe what you are going to find is, for a character, you are going to find in the first position that is not repetitive. Is my understanding correct?
One simple is to use regex to find out such character.
e.g.
shorter but trickier to use regex + split
int position = "aabacbba".split("(?<!a)a(?!a)")[0].length();
or a proper use of matcher:
Matcher m = Pattern.compile("(?<!a)a(?!a)").matcher("aabacbba");
m.find();
int position = m.start();
Both will give you 3
However it is not going to be difficult to use String.chatAt(i) with a bit of logic to find that out
psuedo code
for (int i = 0; i < inputString.length; ++i) {
if (inputString[i] == charToFind
&& inputString[i-1] != charToFind
&& inputString[i+1] != chatToFind) {
return i;
}
}
return -1;
Just a little trap for you to solve by yourself: inputString[i-1] and inputString[i+1] can go out of bound. It should be easy to fix by yourself.
This should work:
private static int findString(String sub, String str, int start) {
boolean isolated = true;
int i = str.indexOf(sub, start);
while (i >= 0) {
int end = i + sub.length();
int next = str.indexOf(sub, end);
if (next < 0 || isolated && next > end) {
break;
}
isolated = next > end;
i = next;
}
return isolated ? i : -1;
}
Using substring() and indexOf() makes this really easy
public static void main(String[] args) throws Exception {
String testString = "aabacbba";
System.out.println(findString(testString, 2, "c"));
}
private static int findString(String stringToSearch, int start, String stringToFind) {
int index = stringToSearch.substring(start).indexOf(stringToFind);
// Add start to the result of indexOf to have the actual index in the original string
// It'll return a -1 if no result is found
return index > -1 ? index + start : index;
}
Results:
findString(testString, 2, "a") => returns 3
findString(testString, 2, "c") => returns 4
findString(testString, 2, "d") => returns -1
findString(testString, 1, "bb") => returns 5
UPDATE
After reading a comment about checking to make sure the character is a single occurrence after the start location, I've made an update answer.
public static void main(String[] args) throws Exception {
String testString = "aabacbba";
System.out.println(findString(testString, 3, "b"));
}
private static int findString(String stringToSearch, int start, String stringToFind) {
int index = stringToSearch.substring(start).indexOf(stringToFind);
// Check the next character to make sure it's a single occurrence
if (index > -1 && stringToSearch.charAt(index + start + 1) == stringToFind.charAt(0)) {
index = -1;
}
// Add start to the result of indexOf to have the actual index in the original string
// It'll return a -1 if no result is found
return index > -1 ? index + start : index;
}
Results:
findString(testString, 3, "b") => returns -1, because b repeats
findString(testString, 2, "a") => returns 3
findString(testString, 2, "c") => returns 4

Recursively counting character occurrences in a string

Im making a program to count the number of times a character is found in a string. This is what my method looks like:
public static int count (String line, char c)
{
int charOccurences = 0; //= 0;
for (int x = 0 ; x < line.length () ; x++)
{
if (line.charAt (x) == c)
{
charOccurences++;
line = line.substring (0, x) + line.substring (x + 1);
return count (line, c);
}
else
return charOccurences;
}
return charOccurences;
}
It always returns 0, due to the fact that once the method calls itself it sets charOccurences back to 0. But i need to declare that variable for the method to work. I cant figure any way around this. Any help would be appreciated.
You ignored charOccurences right after you incremented it.
charOccurences++;
line = line.substring (0, x) + line.substring (x + 1);
return charOccurences + count (line, c); // Fixed for you.
Others have mentioned that you don't need a for loop at all. If you wanted to do this purely recursively, you would simply lose the loop, and follow these steps:
base case:
first character doesn't exist (length is zero)
return 0;
recursion case:
The first character does exist
if it matches, increment occurrences
else do nothing
return (occurrences) + (result of recursing with substring);
Yea, it is very easy to do it recursively :)
public static void main(String[] args) {
String text = "This is my text. Life is great";
System.out.println(count(text,'i',0));
}
public static int count(String line, char c, int pos) {
if (pos >= line.length()){
return 0;
}
return compare(line.charAt(pos), c) + count(line, c, pos+1);
}
public static int compare(char a, char b){
if (a == b){
return 1;
} else {
return 0;
}
}
Note that thanks to not substringing every time, time complexity is O(n) instead of yours O(n^2)
Here's a general approach for writing recursive methods for tasks that really shouldn't be recursive but have to be because you're learning about recursion in class:
Find a way to break the problem down into a smaller problem(s).
Here, your problem is to count the occurrences of character c in a string. Well, suppose you break your string down into "the first character" and a substring of "all the other characters". You can tell whether the first character equals c. Then you look at "all the other characters", and if that's not empty (the base case), then that's just a smaller version of the same problem. So you can use recursion on that. So pretend the recursion already happened, so then you know: (1) is the first character equal to c, and (2) how many occurrences of c are there in the smaller string. Once you know those two pieces of data, you should be able to figure out how many occurrences of c there are in the whole string.
For this problem, your solution should not have a loop in it.
You never actually increment count. You just keep returning count. At the very end of your recursive stack, count will return 0, as that is what you initialize count to at the begining of every method call, and it will keep returning zero until it gets to the bottom of the stack, then return 0. You need to do this:
charOccurences += count (line, c);
return charOccurences;
so charOccurences will start at 1 at the first occurence, then propagate up.
I think you're making it much harder than it needs to be?
public static int count(String line, char c) {
int orig = line.length();
int repl = line.replaceAll("" + c, "").length();
return orig - repl;
}
Despite doing it recursively is not required (let's do it for fun). You were almost done. Just be sure to have a condition that stops the recursion: here it is if (len == 0)… statement.
public static int count (String line, char c)
{
int len = line.length();
if ((len == 0) || (c == '\0')) // obvious case for empty string or nil char.
return 0; // Your recursion always ends here
String rest = line.substring(1);
if (line.charAt(0) == c)
{
return count(rest, c) + 1; // recurse on substring
}
else
{
return count(rest, c); // recurse on substring
}
}
i had the same issue you can always do this i did it on a word same applies for a sentence
private static int count(String word, String letter) {
int count = 0;
return occurrence(word, letter, count);
}
private static int occurrence(String word, String letter, int count) {
if ()
base case
else
// compare and increment if it matches..
return occurrence(word.substring(0, word.length() - 1), letter,count)
}
the other method occurrence be the recursion method,
and repeat your code now count is already defined and you can increment without having any problem! :)
Please remove the else loop inside the for loop. If you keep that loop you should get occurrence of only one character.
public static int count (String line, char c)
{
int charOccurences = 0; //= 0;
for (int x = 0 ; x < line.length () ; x++)
{
if (line.charAt (x) == c)
{
charOccurences++;
line = line.substring (0, x) + line.substring (x + 1);
return count (line, c);
}
}
return charOccurences;
}

Determine if a given string is a k-palindrome

I'm trying to solve the following interview practice question:
A k-palindrome is a string which transforms into a palindrome on removing at most
k characters.
Given a string S, and an integer K, print "YES" if S is a k-palindrome;
otherwise print "NO".
Constraints:
S has at most 20,000 characters.
0 <= k <= 30
Sample Test Cases:
Input - abxa 1
Output - YES
Input - abdxa 1
Output - NO
My approach I've decided is going to be taking all possible String combinations of length s.length - k or greater, i.e. "abc" and k = 1 -> "ab" "bc" "ac" "abc" and checking if they are palindromes. I have the following code so far, but can't seem to figure out a proper way to generate all these string combinations in the general case:
public static void isKPalindrome(String s, int k) {
// Generate all string combinations and call isPalindrome on them,
// printing "YES" at first true
}
private static boolean isPalindrome(String s) {
char[] c = s.toCharArray()
int slow = 0;
int fast = 0;
Stack<Character> stack = new Stack<>();
while (fast < c.length) {
stack.push(c[slow]);
slow += 1;
fast += 2;
}
if (c.length % 2 == 1) {
stack.pop();
}
while (!stack.isEmpty()) {
if (stack.pop() != c[slow++]) {
return false;
}
}
return true;
}
Can anyone figure out a way to implement this, or perhaps demonstrate a better way?
I think there is a better way
package se.wederbrand.stackoverflow;
public class KPalindrome {
public static void main(String[] args) {
KPalindrome kPalindrome = new KPalindrome();
String s = args[0];
int k = Integer.parseInt(args[1]);
if (kPalindrome.testIt(s, k)) {
System.out.println("YES");
}
else {
System.out.println("NO");
}
}
boolean testIt(String s, int k) {
if (s.length() <= 1) {
return true;
}
while (s.charAt(0) == s.charAt(s.length()-1)) {
s = s.substring(1, s.length()-1);
if (s.length() <= 1) {
return true;
}
}
if (k == 0) {
return false;
}
// Try to remove the first or last character
return testIt(s.substring(0, s.length() - 1), k - 1) || testIt(s.substring(1, s.length()), k - 1);
}
}
Since K is max 30 it's likely the string can be invalidated pretty quick and without even examining the middle of the string.
I've tested this with the two provided test cases as well as a 20k characters long string with just "ab" 10k times and k = 30;
All tests are fast and returns the correct results.
This can be solved using Edit distance dynamic programming algorithm. Edit distance DP algorithm is used to find the minimum operations required to convert a source string to destination string. The operations can be either addition or deletion of characters.
The K-palindrome problem can be solved using Edit distance algorithm by checking the minimum operation required to convert the input string to its reverse.
Let editDistance(source,destination) be the function which takes source string and destination string and returns the minimum operations required to convert the source string to destination string.
A string S is K-palindrome if editDistance(S,reverse(S))<=2*K
This is because we can transform the given string S into its reverse by deleting atmost K letters and then inserting the same K letters in different position.
This will be more clear with an example.
Let S=madtam and K=1.
To convert S into reverse of S (i.e matdam) first we have to remove the character 't' at index 3 ( 0 based index) in S.
Now the intermediate string is madam. Then we have to insert the character 't' at index 2 in the intermediate string to get "matdam" which is the reverse of string s.
If you look carefully you will know that the intermediate string "madam" is the palindrome that is obtained by removing k=1 characters.
I found the length of a longest string such that after removing characters >= k, we will be having a palindrome. I have used dynamic programming here. The palindrome I have considered need not be consecutive. Its like abscba has a longest palindromic length of 4.
So now this can be used further, such that whenever k >= (len - len of longest palindrome), it results to true else false.
public static int longestPalindrome(String s){
int len = s.length();
int[][] cal = new int[len][len];
for(int i=0;i<len;i++){
cal[i][i] = 1; //considering strings of length = 1
}
for(int i=0;i<len-1;i++){
//considering strings of length = 2
if (s.charAt(i) == s.charAt(i+1)){
cal[i][i+1] = 2;
}else{
cal[i][i+1] = 0;
}
}
for(int p = len-1; p>=0; p--){
for(int q=p+2; q<len; q++){
if (s.charAt(p)==s.charAt(q)){
cal[p][q] = 2 + cal[p+1][q-1];
}else{
cal[p][q] = max(cal[p+1][q], cal[p][q-1]);
}
}
}
return cal[0][len-1];
}
This is a common interview question, and I'm little surprised that no one has mentioned dynamic programming yet. This problem exhibits optimal substructure (if a string is a k-palindrome, some substrings are also k-palindromes), and overlapping subproblems (the solution requires comparing the same substrings more than once).
This is a special case of the edit distance problem, where we check if a string s can be converted to string p by only deleting characters from either or both strings.
Let the string be s and its reverse rev. Let dp[i][j] be the number of deletions required to convert the first i characters of s to the first j characters of rev. Since deletions have to be done in both strings, if dp[n][n] <= 2 * k, then the string is a k-palindrome.
Base case: When one of the strings is empty, all characters from the other string need to be deleted in order to make them equal.
Time complexity: O(n^2).
Scala code:
def kPalindrome(s: String, k: Int): Boolean = {
val rev = s.reverse
val n = s.length
val dp = Array.ofDim[Int](n + 1, n + 1)
for (i <- 0 to n; j <- 0 to n) {
dp(i)(j) = if (i == 0 || j == 0) i + j
else if (s(i - 1) == rev(j - 1)) dp(i - 1)(j - 1)
else 1 + math.min(dp(i - 1)(j), dp(i)(j - 1))
}
dp(n)(n) <= 2 * k
}
Since we are doing bottom-up DP, an optimization is to return false if at any time i == j && dp[i][j] > 2 * k, since all subsequent i == j must be greater.
Thanks to Andreas, that algo worked like a charm. Here my implementation for anyone who's curious. Slightly different, but fundamentally your same logic:
public static boolean kPalindrome(String s, int k) {
if (s.length() <= 1) {
return true;
}
char[] c = s.toCharArray();
if (c[0] != c[c.length - 1]) {
if (k <= 0) {
return false;
} else {
char[] minusFirst = new char[c.length - 1];
System.arraycopy(c, 1, minusFirst, 0, c.length - 1);
char[] minusLast = new char[c.length - 1];
System.arraycopy(c, 0, minusLast, 0, c.length - 1);
return kPalindrome(String.valueOf(minusFirst), k - 1)
|| kPalindrome(String.valueOf(minusLast), k - 1);
}
} else {
char[] minusFirstLast = new char[c.length - 2];
System.arraycopy(c, 1, minusFirstLast, 0, c.length - 2);
return kPalindrome(String.valueOf(minusFirstLast), k);
}
}
This problem can be solved using the famous Longest Common Subsequence(LCS) method. When LCS is applied with the string and the reverse of the given string, then it gives us the longest palindromic subsequence present in the string.
Let the longest palindromic subsequence length of a given string of length string_length be palin_length. Then (string_length - palin_length) gives the number of characters required to be deleted to convert the string to a palindrome. Thus, the given string is k-palindrome if (string_length - palin_length) <= k.
Let me give some examples,
Initial String: madtam (string_length = 6)
Longest Palindromic Subsequence: madam (palin_length = 5)
Number of non-contributing characters: 1 ( string_length - palin_length)
Thus this string is k-palindromic where k>=1. This is because you need to delete atmost k characters ( k or less).
Here is the code snippet:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 10000
int table[MAX+1][MAX+1];
int longest_common_subsequence(char *first_string, char *second_string){
int first_string_length = strlen(first_string), second_string_length = strlen(second_string);
int i, j;
memset( table, 0, sizeof(table));
for( i=1; i<=first_string_length; i++ ){
for( j=1; j<=second_string_length; j++){
if( first_string[i-1] == second_string[j-1] )
table[i][j] = table[i-1][j-1] + 1;
else
table[i][j] = max(table[i-1][j], table[i][j-1]);
}
}
return table[first_string_length][second_string_length];
}
char first_string[MAX], second_string[MAX];
int main(){
scanf("%s", first_string);
strcpy(second_string, first_string);
reverse(second_string, second_string+strlen(second_string));
int max_palindromic_length = longest_common_subsequence(first_string, second_string);
int non_contributing_chars = strlen(first_string) - max_palindromic_length;
if( k >= non_contributing_chars)
printf("K palindromic!\n");
else
printf("Not K palindromic!\n");
return 0;
}
I designed a solution purely based on recursion -
public static boolean isKPalindrome(String str, int k) {
if(str.length() < 2) {
return true;
}
if(str.charAt(0) == str.charAt(str.length()-1)) {
return isKPalindrome(str.substring(1, str.length()-1), k);
} else{
if(k == 0) {
return false;
} else {
if(isKPalindrome(str.substring(0, str.length() - 1), k-1)) {
return true;
} else{
return isKPalindrome(str.substring(1, str.length()), k-1);
}
}
}
}
There is no while loop in above implementation as in the accepted answer.
Hope it helps somebody looking for it.
public static boolean failK(String s, int l, int r, int k) {
if (k < 0)
return false;
if (l > r)
return true;
if (s.charAt(l) != s.charAt(r)) {
return failK(s, l + 1, r, k - 1) || failK(s, l, r - 1, k - 1);
} else {
return failK(s, l + 1, r - 1, k);
}
}

I need to use substring, but I need the second parameter to be inclusive

I've got a String that I need to cycle through and create every possible substring. For example, if I had "HelloWorld", "rld" should be one of the possibilities. The String method, substring(int i, int k) is exclusive of k, so if
|H|e|l|l|o|W|o|r|l|d|
0 1 2 3 4 5 6 7 8 9
then
substring(7,9) returns "rl"
How would I work around this and get it to work inclusively? I understand why a substring shouldn't be able to equal the String it was created from, but in this case it would be very helpful to me in this case.
Example from Codingbat: http://codingbat.com/prob/p137918
What I was able to come up with:
public String parenBit(String str) {
String sub;
if (str.charAt(0) == '(' && str.charAt(str.length() - 1) == ')')
return str;
for (int i = 0; i < str.length() - 1; i++) {
for (int k = i + 1; k < str.length(); k++) {
sub = str.substring(i,k);
}
}
return null;
}
The transformation between exclusive to inclusive is simple when you're working in integers. You just add 1.
String substringInclusive(String s, int a, int b)
{
return s.substring(a, b+1);
}
As Jon Skeet rightly pointed out that adding 1 would be the right thing to do as the second parameter in String.substring is not inclusive.
However your answer is not recursive, below is the recursive solution:
public String parenBit(String str) {
if(str.charAt(0)!='(')
return parenBit(str.substring(1));
if(str.charAt(0)=='('&&(str.charAt(str.length()-1)!=')'))
return parenBit(str.substring(0, str.length()-1));
return str;
}

Categories