Removing substring from string using recursion - java

I am trying to solve a question where I need to remove specific substring from string using recursion.
I was able to solve this using a do while loop but that is not the best approach.
For example,
Suppose I have a string named, DADDAUUPPA and if I am given set of substrings like "DD", "UU","PP", "AA" then the returned string should be DA because, DADDAUUPPA -> DAAUUPPA -> DUUPPA -> DPPA -> DA
Here is my solution doing it using while loop.
String s = "DADDAUUPPA";
do{
String original = s;
return original.replace("DD","").replace("UU","").replace("PP","").replace("AA","" );
} while(!original)
Other challenging task is what if we are given these substrings as an array, how would we do it then?
For example,
public soln (String[] sub) {
// sub contains all the substring to be removed
}

Here is a solution
import java.util.LinkedList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> subs = new LinkedList<>();
subs.add("DD");
subs.add("AA");
subs.add("UU");
subs.add("PP");
String result = getSubString(subs, "DADDAUUPPA ");
System.out.print(result);
}
private static String getSubString(List<String> subs, String myString) {
if(subs !=null && subs.size() != 0) {
String sub = subs.get(0);
subs.remove(0);
myString = getSubString(subs, myString.replace(sub, ""));
}
return myString;
}
}
Key thing to learn is
private static String getSubString(List<String> subs, String myString) {
if(subs !=null && subs.size() != 0) {
myString = myString.replace(subs.get(0), "");
subs.remove(0);
getSubString(subs, myString);
}
return myString;
}
This does not work, note the difference between first and second getSubString method. As the stack unwinds myString gets assigned to original value.

Related

Problem with recursive method in reversing the string

When I am reversing a String using the recursive method it is giving me the stackOverflow Error.
public class ReverseString {
public static void main(String[] args) {
String str = "Juhi";
System.out.println(recursiveString(str));
}
static String recursiveString(String str) {
return !str.equals("") ? recursiveString(str.substring(1) + str.charAt(0)) : str;
}
}
This should fix the problem:
static String recursiveString(String str) {
return !str.equals("") ? recursiveString(str.substring(1)) + str.charAt(0) : str;
}
In your code, you're not reducing the length of the string parameter, it's always the same input, so basically it's entering into an infinite loop. If we move the concatenation part after the recursive call, then the string will be shorter for each recursive call, until it's empty, signalling the end of the recursion.
public static void main(String[] args) {
String str = "Juhi";
System.out.println(recursiveString(str));
}
static String recursiveString(String str) {
return !str.equals("") ? recursiveString(str.substring(1) ) + str.charAt(0): str;
}
}
A Unicode-aware recursive reverse
While the other answers have shown you where you went wrong and how to write a recursive string reversing algorithm that works for basic Unicode characters, they produce wrong results for supplementary Unicode characters. The following method works for all Unicode characters:
static String recursiveReverse(String str) {
if (str.isEmpty())
return "";
int offsetToSecondCodePoint = str.offsetByCodePoints(0,1);
return recursiveReverse(str.substring(offsetToSecondCodePoint))
+ str.substring(0, offsetToSecondCodePoint);
}

Working with Substring In JAVA from right hand direction

Is it possible to get substring from right hand hand(Reverse) direction using substring() in JAVA.
Example.
Suppose String S="abcdef",
Can I get Substring "fedc" using S.substring(S.length()-1,3) ?
If it is not correct, please suggest me how to get Substring from right hand end(Reverse direction)??
You could reverse the string and use substring. Unfortunately String does not have that, but StringBuilder has it e.g.
new StringBuilder("abcdef").reverse().toString().substring(0,4);
You can reverse the string and find the substring
// reverse
String s = "abcdef";
StringBuilder builder = new StringBuilder(s);
String substring = builder.reverse().substring(0,3);
Java doesn't support extension methods like C# does, so I would build a function for this. This way you can control how much of the reverse substring you want with a parameter.
public class StackOverflow {
public static void main(String[] args) {
String data = "abcdef";
for (int i = 0; i < data.length(); i++) {
System.out.println(reverseSubstring(data, i+1));
}
}
public static String reverseSubstring(String data, int length) {
return new StringBuilder(data).reverse().substring(0, length);
}
}
Result:
f
fe
fed
fedc
fedcb
fedcba
UPDATE
Another approach is to create a wrapper class to String. This way you can call a class method like how you're asking in your question with the example S.substring(S.length()-1,3). This will also allow you to still have all the String methods after using the wrapper's get() method.
String Wrapper
public class MyString {
private String theString;
public MyString(String s) {
theString = s;
}
public String get() {
return theString;
}
public String reverseSubstring(int length) {
return new StringBuilder(theString).reverse().substring(0, length);
}
}
Usage
public class StackOverflow {
public static void main(String[] args) {
MyString data = new MyString("abcdef");
for (int i = 0; i < data.get().length(); i++) {
System.out.println(data.reverseSubstring(i+1));
}
}
}
Results:
f
fe
fed
fedc
fedcb
fedcba

Is there an elegant way to get fist two consecutive characters from a String until fist digit occurrence

I want to get the fist two characters (not digits) from a String until the first digit occurance. String may be any and would contains digits too. I just have to care first two indexes only. Bit tricky part is let say the second index contains a digit, then only the first character need to consider.
Examples:
abcd -> ab
a -> a
a0cd -> a
0bcd -> null
-123 -> null
Below is how I wrote this function in java. Is there any other elegant way to do this? Any help is much appreciated.
public class Main {
public static String getFirstTwoCharBeforeDigit(String s) {
if(null==s||s.length()==0) return null;
int cropIndex=Math.min(s.length(), 2);
if(!Character.isLetter(s.charAt(0))) return null;
if(cropIndex>1 && !Character.isLetter(s.charAt(1))) --cropIndex;
return s.substring(0,cropIndex);
}
public static void main(String[] args) {
System.out.println(getFirstTwoCharBeforeDigit("Az-a0"));
}
}
This seems to work (thank you Timothy):
private static final java.util.regex.Pattern pattern = Pattern.compile("^[a-z]{1,2}");
private static String getFirstTwoChars(String string) {
if (string == null) {
return null;
}
java.util.regex.Matcher matcher = pattern.matcher(string);
return matcher.find() ? matcher.group(0) : null;
}
I'd suggest the use of regular expressions as described in the API of the Pattern class.
"^[a-z]{1,2}"
#RunWith(Parameterized.class)
public class ConsecutiveCharsTest {
#Parameters
public static Collection<Object[]> data() {
//#formatter:off
return Arrays.asList(new Object[][] {
{"abcd", "ab" },
{"a", "a" },
{"a0bc", "a" },
{"0bcd", null },
{"-123", null },
});
//#formatter:on
}
private final String input;
private final String expected;
public ConsecutiveCharsTest(String input, String expected) {
super();
this.input = input;
this.expected = expected;
}
#Test
public void test() {
Pattern pattern = Pattern.compile("^[a-z]{1,2}");
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
assertEquals(input, expected, matcher.group());
} else {
assertNull("no Match expected", expected);
}
}
}
I think that using RegExp for this simple task is too heavy, because lot's of additional objects are created behind the scene. My solution is less readable, but it has better performance and minimal additional memory objects creation:
public static String getFirstTwoCharBeforeDigit(String str) {
return str != null && !str.isEmpty() ? Character.isAlphabetic(str.charAt(0)) ? str.substring(0, str.length() > 1 && Character.isAlphabetic(str.charAt(1)) ? 2 : 1) : null : null;
}
Just to add one more alternative:
public static String getFirstTwoCharBeforeDigit(String str) {
String firstTwo = str.substring(0,Math.min(str.length(), 2)).replaceAll("\\d*$", "");
return firstTwo.chars().allMatch(Character::isLetter) && !firstTwo.isEmpty()?
firstTwo:
null;
}

How to enter arrays of varried dimensions into a method in java

My java program formats data from a string array into a string and can basically pull an inception and store these strings along other strings of the same format, in a way making a 2D array but in a string format. My issue is for a 1D array I roughly have the following methods in a class...
public static String parse(String data) {
return "#"+Integer.toString(data.length())+":"+data;
}
public static String parse(String[] data) {
String str="";
for (int i=1;i<=data.length;i++) {
str+=parse(data[i-1]);//parse() would receive a String
}
return str;
}
My problem is I want to make a singular method that can take in an array of ANY dimensions, but the closest I can get to doing this is declaring multiple methods with the same name but with higher array dimensions set for the input variable 'data' like so...
public static String parse(String[][] data) {//can take in a 2D array
String str="";
for (int i=1;i<=data.length;i++) {
str+=parse(data[i-1]);//parse() would receive a 1D array
}
return str;
}
public static String parse(String[][][] data) {//can take in a 3D array
String str="";
for (int i=1;i<=data.length;i++) {
str+=parse(data[i-1]);//parse() would receive a 2D array
}
return str;
}
//etc...
Is there a way to work arround this? Or is this the best way to program it?
PS:I'm new to this language and still dont know a lot of terminology, so keep it simple please.
The problem is the difference in types, 1 parse method takes a String[][][] and calls another parse method that takes a String[][]. That second call is different for each level, so you can't create 1 single method that works for all of them.
Something that you can do (but will be tricky to grasp for a beginner) is reduce some of the duplication. If you pass the next parse method to call as an argument to parse. You can then use lambdas to chain method calls together:
public static String parse0(String data) {
return "#" + Integer.toString(data.length()) + ":" + data;
}
// Method that contains the real logic
private static <T> String parseInternal(T[] data, Function<T, String> parser) {
String str = "";
for (int i = 0; i < data.length; i++) {
str += parser.apply(data[i]);
}
return str;
}
// Methods that provide the call chain:
public static String parse3(String[][][] data) {
return parseInternal(data, arr2 -> parse2(arr2));
}
public static String parse2(String[][] data) {
return parseInternal(data, arr -> parse1(arr));
}
public static String parse1(String[] data) {
return parseInternal(data, str -> parse0(str));
}
Note: I gave all the methods a different name to show better what is going on. But you could give all of them the same name as well.
For more information about lambda expressions see the Oracle tutorial.
The solution is called recursion: that a method sometimes calls itself for doing some of its task:
public static String parse(Object data) {
if (data instanceof String) {
return "#" + Integer.toString(((String) data).length()) + ":" + data;
}
if (data instanceof Object[]) {
String str = "";
for (Object obj : (Object[]) data) {
str += parse(obj);
}
return str;
}
throw new IllegalArgumentException("Cannot parse type " + data.getClass());
}
Now we may do for instance:
System.out.println(parse("str"));
System.out.println(parse(new String[] { "abc", "def" }));
System.out.println(parse(new String[][] { { "gh", "ij" }, { "kl" } }));
System.out.println(parse(new String[][][] { { { "mn" }, { "op", "qr", "st" }, { "uv" } } }));
This prints:
#3:str
#3:abc#3:def
#2:gh#2:ij#2:kl
#2:mn#2:op#2:qr#2:st#2:uv
In fact you can call it with a String array with any number of dimension.

Check that word all words from one string exist in the other

How can I check that all the words from string #2 exist in String #1? It should be case insensitive and I want exclude all punctuation and special characters during comparison of words.
Any help?
Thanks.
Algorithm
Iterate through words in String #1 and insert them as keys into a dictionary/hash/associative array.
Iterate through words in String #2 and check if each word is a key in the dictionary created in step 1.
If one is not found, return false.
After the iteration has finished, return true.
Running time: O(n)
I'll let someone else implement this in Java.
To find the words in a String while ignoring the various punctuations etc you can use the StringTokenizer class.
StringTokenizer st = new StringTokenizer("Your sentence;with whatever. punctuations? might exists", " :?.,-+=[]");
This breaks up the String into Tokens using the delimiters provided in the second example. You can then use hasMoreTokens() and nextToken() method to iterate the tokens.
Then you can use the algorithm suggested by #MattDiPasquale.
You can try String's built-in split method
 
it looks like
public String[] split(String regex)
and it returns an array of Strings based on the regular expression you use. There are examples in the link above.
You can easily generate two arrays this way (one for String #1 and one for String #2).
Sort the arrays and then check if the arrays are equal. (size and order)
You can simplify array sorting if you utilize java.util.Arrays
Arrays in Java have a lot of library methods and you should learn about them because they are incredibly useful sometimes:
http://leepoint.net/notes-java/data/arrays/arrays-library.html
This is slightly less efficient than building a dictionary/hash table/ADT with your selected delimiters (like in MattDiPasquale's answer), but it might be easier to understand if you are not very familiar with hash functions or dictionaries (as a datatype).
isContainsAll(s1, s2)
1 . split s2 by " "; s.split("")
2 . check if s1 contains all the element of s2
public static boolean isContainsAll(String s1, String s2){
String[] split = s2.split(" ");
for(int i=0; i<split.length; i++){
if(!s1.contains(split[i])){
return false;
}
}
return true;
}
public static void main(String... args){
System.out.println(isContainsAll("asd dsasda das asd; asds asd;/ ", "asd;/"));
}
While the algorithm to do this is simple, the implementation is more involved if you want to support multiple locales. Below is a sample code that supports multiple locales. I've verified this with English as well as Chinese (But I am not sure if it passes the Turkey Test ;-)). Anyways the below code needs some refactoring but this will get you started.
NOTE: Even if you doesn't want support for other languages than English, I still would use the below as the word boundarie/punctuations/grammar etc are locale/language dependent which might not be well addressed by StringTokenizer, String.split(...) and other basic APIs.
import java.text.BreakIterator;
import java.text.Collator;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang.StringEscapeUtils;
public class UnicodeWordCount
{
public static void main(final String[] args)
{
testEnglish();
testChinese();
}
public static void testEnglish()
{
BreakIterator wordIterator = BreakIterator.getWordInstance(Locale.ENGLISH);
String str = "This is the source string";
String match = "source string is this";
String doesntMatch = "from Pangea";
Set<String> uniqueWords = extractWords(str, wordIterator, Locale.ENGLISH);
printWords(uniqueWords);
System.out.println("Should print true: " + contains(match, wordIterator, uniqueWords));
System.out.println("Should print false: " + contains(doesntMatch, wordIterator, uniqueWords));
}
public static void testChinese()
{
BreakIterator wordIterator = BreakIterator.getWordInstance(Locale.CHINESE);
String str = "\u4E0D\u70BA\u6307\u800C\u8B02\u4E4B\u6307\uFF0C\u662F[\u7121\u90E8]\u70BA\u6307\u3002\u201D\u5176\u539F\u6587\u70BA";
String match = "\u5176\u539F\u6587\u70BA\uFF0C\u70BA\u6307";
String doesntMatch = "\u4E0D\u70BA\u6307\u800C\u8B02\u4E4B\u6307\uFF0C\u662F[\u517C\u4E0D]\u70BA\u6307\u3002";
Set<String> uniqueWords = extractWords(str, wordIterator, Locale.CHINESE);
printWords(uniqueWords);
System.out.println("Should print true: " + contains(match, wordIterator, uniqueWords));
System.out.println("Should print false: " + contains(doesntMatch, wordIterator, uniqueWords));
}
public static Set<String> extractWords(final String input, final BreakIterator wordIterator, final Locale desiredLocale)
{
Collator collator = Collator.getInstance(desiredLocale);
collator.setStrength(Collator.PRIMARY);
Set<String> uniqueWords = new TreeSet<String>(collator);
wordIterator.setText(input);
int start = wordIterator.first();
int end = wordIterator.next();
while (end != BreakIterator.DONE)
{
String word = input.substring(start, end);
if (Character.isLetterOrDigit(word.charAt(0)))
{
uniqueWords.add(word);
}
start = end;
end = wordIterator.next();
}
return uniqueWords;
}
public static boolean contains(final String target, final BreakIterator wordIterator, final Set<String> uniqueWords)
{
wordIterator.setText(target);
int start = wordIterator.first();
int end = wordIterator.next();
while (end != BreakIterator.DONE)
{
String word = target.substring(start, end);
if (Character.isLetterOrDigit(word.charAt(0)))
{
if (!uniqueWords.contains(word))
{
return false;
}
}
start = end;
end = wordIterator.next();
}
return true;
}
private static void printWords(final Set<String> uniqueWords)
{
for (String word : uniqueWords)
{
System.out.println(StringEscapeUtils.escapeJava(word));
}
}
}

Categories

Resources