JAVA: comparing a String with a SubString - java

So here's what I'm trying to accomplish. I'm trying to make a code that does the following from 2 given Strings: a target and a source.
// Determines whether the string TARGET occurs as a substring of string SOURCE where "gaps" are allowed between characters of target.`
// That is, the characters in TARGET occur in SOURCE in their given order but do not have to be adjacent.`
// (Pictured another way, this method returns true if TARGET could be obtained from SOURCE by removing some of the letters of SOURCE.)`
// This method is case sensitive. For example,`
// containsWithGaps("hamburgers", "mug") returns true`
// containsWithGaps("hamburgers", "burrs") returns true`
// containsWithGaps("hamburgers", "hamburgers") returns true`
// containsWithGaps("hamburgers", "gum") returns false`
// containsWithGaps("hamburgers", "hamm") returns false`
// containsWithGaps("hamburgers", "") returns true`
// Parameters:`
// SOURCE - the given string in which to find the target characters`
// TARGET - the characters to be found`
// Returns:`
// true if the characters in TARGET can be found as a subsequence in SOURCE, false otherwise`
And here's the code I've written. It seems to be overly complicated for what I believe shouldn't be a difficult task, but no matter what, I still keep getting errors and it won't work if given a SOURCE string hamburgers with a TARGET string burr:
public static boolean substringWithGaps(String source, String target) {
boolean substring = false;
int[] target_index;
target_index = new int [target.length()];
if (target.length() > source.length()) {
substring = false;
}
else {
for (int i = 0; i < target.length(); i++) {
if (source.contains("" + target.charAt(i))) {
target_index[i] = target.indexOf(i);
i++;
}
else {
target_index[i] = target.indexOf(i);
i++;
}
}
for (int i = 0; i < target_index.length; i++) {
if (target_index[i] == -1) {
substring = false;
break;
}
else if (target_index[i] >= target_index[i+1]) {
substring = false;
break;
}
else {
substring = true;
}
if (target_index.length != target.length()) {
substring = false;
}
}
}
return substring;
}
Any ideas?

Should be pretty simple:
public static boolean substringWithGaps(String source, String target) {
int targetIndex = 0;
for (int i = 0; i < source.length(); i++) {
if (source.charAt(i) == target.charAt(targetIndex)) {
targetIndex = targetIndex + 1;
if (targetIndex == target.length()) {
return true;
}
}
}
return false;
}
We keep an index of the next letter we need to find within target. Then we loop over source looking for that letter, and when we find it we move the index within target forward one. If the index of target ever equals the length of target, that means we found all of the characters we needed. If we loop over all of source without finding all of target, we return false.

The following should do it.
public static boolean containsWithGaps(String a, String b){
if(b.length() > a.length())
{
return false;
}
char[] targetChars = new char[b.length()];
b.getChars(0,b.length(),targetChars, 0);
int pos = 0;
for(char myChar : targetChars)
{
pos = a.indexOf(myChar, pos);
if(pos == -1)
{
return false;
}
}
return true;
}

Slight optimization in that it returns as soon as a character could not be matched (and doesn't crash if target is zero length)
public static boolean substringWithGaps(String source, String target) {
for (int i = 0, last = -1; i < target.length(); i++) {
if (-1 == (last = source.indexOf(target.charAt(i), last + 1)))
return false;
}
return true;
}

Related

Given two strings S and T, return if they are equal when both are typed into empty text editors. # means a backspace character

Example 1:
Input: S = "ab#c", T = "ad#c"
Output: true
Explanation: Both S and T become "ac".
Example 2:
Input: S = "ab##", T = "c#d#"
Output: true
Explanation: Both S and T become "".
Example 3:
Input: S = "a##c", T = "#a#c"
Output: true
Explanation: Both S and T become "c".
Example 4:
Input: S = "a#c", T = "b"
Output: false
Explanation: S becomes "c" while T becomes "b".
class Solution {
public boolean backspaceCompare(String S, String T) {
Stack<Character> stack1 = new Stack<Character>();
Stack<Character> stack2 = new Stack<Character>();
for(int i=0;i<S.length();i++){
if(S.charAt(i)!='#'){
stack1.push(S.charAt(i));
}else{
stack1.pop();
}
}
for(int j =0;j<T.length();j++){
if(T.charAt(j)!='#'){
stack2.push(S.charAt(j));
}else
stack2.pop();
}
if(stack1==stack2)
return true;
return false;
}
}
my output is false and answer should be true why is this not working?
The first mistake is pushing all the characters on the stack outside of the if statement.
Also you should check if stack is empty before removing items from it.
Otherwise EmptyStackException is thrown.
// stack1.push(S.charAt(i)); <-- remove this line
if (S.charAt(i)!='#') {
stack1.push(S.charAt(i));
}else if (!stack1.isEmpty()) { // <-- add this check
stack1.pop();
}
The second mistake is you can't use == to compare the contents of two stacks, use .equals method instead:
if(stack1.equals(stack2))
Answer by Joni correctly addresses the errors in the code, however there are some other issues I'd like to address:
You should use a helper method to eliminate repeating the same code.
You should use Deque instead of Stack. The javadoc says so.
Instead of using Stack/Deque, I'd recommend using StringBuilder, to prevent having to box the char values.
Something like this:
public boolean backspaceCompare(String s, String t) {
return applyBackspace(s).equals(applyBackspace(t));
}
private static String applyBackspace(String s) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != '#')
buf.append(s.charAt(i));
else if (buf.length() != 0)
buf.setLength(buf.length() - 1);
}
return buf.toString();
}
Your idea works, but it's expensive and unnecessary to copy the strings into stacks. If you work backwards from the end, no extra storage is necessary:
//given the string length or a valid character position, return
//the position of the previous valid character, or -1 if none
public static int previousCharPos(String s, int pos)
{
int bs=0; // number of backspaces to match
while(pos>0) {
--pos;
if (s.charAt(pos)=='#') {
++bs;
} else if (bs <= 0) {
return pos;
} else {
--bs;
}
}
return -1;
}
public static boolean backspaceCompare(String S, String T)
{
int spos = previousCharPos(S,S.length());
int tpos = previousCharPos(T,T.length());
while(spos >= 0 && tpos >= 0) {
if (S.charAt(spos) != T.charAt(tpos)) {
return false;
}
spos = previousCharPos(S,spos);
tpos = previousCharPos(T,tpos);
}
return spos == tpos;
}

Search a string for a specified substring using recursion

I'm working on a short project to search a string for a specified substring using recursion.
I have tried using various strings and substrings, as well as making my code as simple as possible, but it always returns false if the substring is more than one character. (I have an accessor and mutator, as well as int i set to 0 before this method)
public boolean find(String target) {
if (i == target.length()) {
return true;
}
System.out.println(sentence);
if (sentence.length() < target.length()) {
return false;
}
if (getSentence().toLowerCase().charAt(0) == target.toLowerCase().charAt(0)) {
i++;
} else {
i = 0;
}
sentence = sentence.substring(1);
return find(target);
}
Tester code and output:
public static void main(String[] args) {
Sentence test = new Sentence("Lizard");
System.out.println(test.find("z"));
Sentence test2 = new Sentence("Seventeen");
System.out.println(test2.find("teen"));
}
Lizard
izard
zard
true
Seventeen
eventeen
venteen
enteen
nteen
teen
een
false
Your method only tests target at the first character, but you modify the sentence - e.g. you also need to modify your target when you recurse. Something like,
public boolean find(String target) {
if (i == target.length()) {
return true;
}
System.out.println(sentence);
if (sentence.length() < target.length()) {
return false;
}
if (sentence.toLowerCase().charAt(0) == target.toLowerCase().charAt(0)) {
i++;
} else {
i = 0;
}
sentence = sentence.substring(1);
return find(target.substring(1));
}

Check if letter 1 is always followed by number 2 and number 2 by number 3

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

JAVA :How to determine the index of the next character in a string NOT equal to a set of delimiters

I don't think this question is very complicated to an experienced programmer, but I'm pretty new to it so I'm struggling.
I have a list of delimiters declared before a java class as such:
public static final String DELIMITERS = ",<.>/?;:'\"[{]}\\|=+-_)(*&^%$##!`~ \t\n";
I'd like to create a method that takes two parameters (a starting index and a string). The goal is to read through the string and return the next index that corresponds to a character NOT in the list of delimiters given above. If the starting index is a negative number or greater than the length of the text, the method should simply return -1. Otherwise it just returns the index of the next character NOT in the delimiter list.
This is what I have so far:
public static boolean isDelimiter(char c) {
String letter = "" + c;
if(DELIMITERS.contains(letter)){
return true;
}
else{
return false;
}
}
public static int posNextWord(int startPosition, String text) {
boolean isWord = false;
int nextWordPosition = 0;
if(startPosition < 0 || startPosition > (text.length()-1)){
return -1;
}
else{
while(isWord = false) {
for (int i = startPosition; i < text.length(); i++) {
if(!isDelimiter(text.charAt(i))){
nextWordPosition = nextWordPosition + i + startPosition;
isWord = true;
}
else{
isWord = false;
}
}
}
return nextWordPosition;
}
}
}
When I run this program with a sample text and index, however, the method just returns the number 0. Any help at all would be much appreciated. Also, the method isDelimiter is required for use in the posNextWord() method.
This entire block of code is the problem:
while(isWord = false) {
for (int i = startPosition; i < text.length(); i++) {
if(!isDelimiter(text.charAt(i))){
nextWordPosition = nextWordPosition + i + startPosition;
isWord = true;
}
else{
isWord = false;
}
}
}
return nextWordPosition;
First of all, you only need one loop to check each character of your loop. You don't need a while and a for. Second, if you want to find the first non-matching char, you can just return when you find it.
Like this:
for (int i = startPosition; i < text.length(); i++) {
if(!isDelimiter(text.charAt(i))){
return i + startPosition;
}
}
return -1;
See documented comments. Don't hesitate to ask if it is not clear :
public static boolean isDelimiter(char c) {
String letter = "" + c;
if(DELIMITERS.contains(letter)){
return true;
}
//else{ this else is not needed
System.out.println(letter +" is not a delimiter");
return false;
//}
}
public static int posNextWord(int startPosition, String text) {
//boolean isWord = false; not used
int nextWordPosition = 0;
if((startPosition < 0) || (startPosition > (text.length()-1))){
return -1;
}
//else{ this is not needed
//while(isWord = false) {
for (int i = startPosition; i < text.length(); i++) {
if(!isDelimiter(text.charAt(i))){
nextWordPosition = nextWordPosition + i + startPosition;
//isWord = true;
return nextWordPosition; //as Scary Wombat commented
}
//else{
// isWord = false;
//}
}
//isWord = false;
//}
return nextWordPosition;
// }
}
BTW: I don't think it is a good idea to return 0 as "not found" result. 0 may be returned also as valid "found" position.

Trying to return true if all the letters in a string are the same

What I have so far:
public boolean allSameLetter(String str)
{
for (int i = 1; i < str.length(); i++)
{
int charb4 = i--;
if ( str.charAt(i) != str.charAt(charb4))
{
return false;
}
if ( i == str.length())
{
return true;
}
}
}
Please excuse any inefficiencies if any; still relatively new to coding in general. Am I lacking some knowledge in terms of using operators and .charAt() together? Is it illogical? Or is my error elsewhere?
Using regex:
return str.matches("^(.)\\1*$");
Using streams:
str.chars().allMatch(c -> c == str.charAt(0));
Other:
return str.replace(String.valueOf(str.charAt(0), "").length() == 0;
You can follow the below steps:
(1) Get the first character (i.e., 0th index)
(2) Check the first character is the same with subsequent characters, if not return false (and comes out from method)
(3) If all chars match i.e., processing goes till the end of the method and returns true
public boolean allSameLetter(String str) {
char c1 = str.charAt(0);
for(int i=1;i<str.length;i++) {
char temp = str.charAt(i);
if(c1 != temp) {
//if chars does NOT match,
//just return false from here itself,
//there is no need to verify other chars
return false;
}
}
//As it did NOT return from above if (inside for)
//it means, all chars matched, so return true
return true;
}
As Andrew said, you are decreasing i within your for loop. You can fix this by changing it to int charb4 = i - 1;. As for making your code more efficient you could condense it down to this.
public boolean allSameLetter(String str) {
for(char c : str.toCharArray())
if(c != str.charAt(0)) return false;
return true;
}
Comment if you don't understand a part of it :)
public boolean allSameLetter(String str)
{
for (int i = 1; i < str.length() -1; i++)
{
if ( str.charAt(i) != str.charAt(i+1))
{
return false;
}
}
return true
}
-1 is there since I am checking the current value in the array, then the next value in the array, thus I need to stop a place earlier.
If the loop if statement is never entered, it will make it far enough into the code to return true
You have to create a for loop that searches through the length of the String - 1. this way the program will not crash because of a 3 letter word with the program trying to get the 4th letter. This is what works for me:
public boolean allSameLetter(String str)
{
for(int i = 0; i< str.length()-1; i++){
if (str.charAt(i) != str.charAt(i+1)){
return false;
}
}
return true;
}
if((new HashSet<Character>(Arrays.asList(s.toCharArray()))).size()==1)
return true;
return false;
This should be enough
The bug is caused by
int charb4 = i--;
this line is equal to
int charb4 = i-1;
i=i-1;
Because of this, your loop will never stop.
The easiest way to fix this
public boolean allSameLetter(String str)
{
for (int i = 1; i < str.length(); i++)
{
if ( str.charAt(i) != str.charAt(i-1))
{
return false;
}
}
}

Categories

Resources