Related
Suppose we have a string of binary values in which some portions may correspond to specific letters, for example:
A = 0
B = 00
C = 001
D = 010
E = 0010
F = 0100
G = 0110
H = 0001
For example, if we assume the string "00100", we can have 5 different possibilities:
ADA
AF
CAA
CB
EA
I have to extract the exact number of combinations using Dynamic programming.
But I have difficulty in the formulation of subproblems and in the composition of the corresponding vector of solutions.
I appreciate any indications of the correct algorithm formulation.
class countString {
static int count(String a, String b, int m, int n) {
if ((m == 0 && n == 0) || n == 0)
return 1;
if (m == 0)
return 0;
if (a.charAt(m - 1) == b.charAt(n - 1))
return count(a, b, m - 1, n - 1) +
count(a, b, m - 1, n);
else
return count(a, b, m - 1, n);
}
public static void main(String[] args) {
Locale.setDefault(Locale.US);
ArrayList<String> substrings = new ArrayList<>();
substrings.add("0");
substrings.add("00");
substrings.add("001");
substrings.add("010");
substrings.add("0010");
substrings.add("0100");
substrings.add("0110");
substrings.add("0001");
if (args.length != 1) {
System.err.println("ERROR - execute with: java countString -filename- ");
System.exit(1);
}
try {
Scanner scan = new Scanner(new File(args[0])); // not important
String S = "00100";
int count = 0;
for(int i=0; i<substrings.size(); i++){
count = count + count(S,substrings.get(i),S.length(),substrings.get(i).length());
}
System.out.println(count);
} catch (FileNotFoundException e) {
System.out.println("File not found " + e);
}
}
}
In essence, Dynamic Programming is an enhanced brute-force approach.
Like in the case of brute-force, we need to generate all possible results. But contrary to a plain brute-force the problem should be divided into smaller subproblems, and previously computed result of each subproblem should be stored and reused.
Since you are using recursion you need to apply so-called Memoization technic in order to store and reuse the intermediate results. In this case, HashMap would be a perfect mean of storing results.
But before applying the memoization in order to understand it better, it makes sense to start with a clean and simple recursive solution that works correctly, and only then enhance it with DP.
Plain Recursion
Every recursive implementation should contain two parts:
Base case - that represents a simple edge-case (or a set of edge-cases) for which the outcome is known in advance. For this problem, there are two edge-cases: the length of the given string is 0 and result would be 1 (an empty binary string "" results into an empty string of letters ""), another case is when it's impossible to decode a given binary string and result will be 0 (in the solution below it resolves naturally when the recursive case is being executed).
Recursive case - a part of a solution where recursive calls a made and when the main logic resides. In the recursive case, we need to find each binary "binary letter" at the beginning of the string and then call the method recursively by passing the substring (without the "letter"). Results of these recursive calls need to be accumulated in the total count that will returned from the method.
In order to implement this logic we need only two arguments: the binary string to analyze and a list of binary letters:
public static int count(String str, List<String> letters) {
if (str.isEmpty()) { // base case - a combination was found
return 1;
}
// recursive case
int count = 0;
for (String letter: letters) {
if (str.startsWith(letter)) {
count += count(str.substring(letter.length()), letters);
}
}
return count;
}
This concise solution is already capable of producing the correct result. Now, let's turn this brute-force version into a DP-based solution, by applying the memoization.
Dynamic Programming
As I've told earlier, a HashMap will be a perfect mean to store the intermediate results because allows to associate a count (number of combinations) with a particular string and then retrieve this number almost instantly (in O(1) time).
That how it might look like:
public static int count(String str, List<String> letters, Map<String, Integer> vocab) {
if (str.isEmpty()) { // base case - a combination was found
return 1;
}
if (vocab.containsKey(str)) { // result was already computed and present in the map
return vocab.get(str);
}
int count = 0;
for (String letter: letters) {
if (str.startsWith(letter)) {
count += count(str.substring(letter.length()), letters, vocab);
}
}
vocab.put(str, count); // storing the total `count` into the map
return count;
}
main()
public static void main(String[] args) {
List<String> letters = List.of("0", "00", "001", "010", "0010", "0100", "0110", "0001"); // binary letters
System.out.println(count("00100", letters, new HashMap<>())); // DP
System.out.println(count("00100", letters)); // brute-force recursion
}
Output:
5 // DP
5 // plain recursion
A link to Online Demo
Hope this helps.
Idea is to create every possible string with these values and check whether input starts with the value or not. If not then switch to another index.
If you have test cases ready with you you can verify more.
I have tested only with 2-3 values.
public int getCombo(String[] array, int startingIndex, String val, String input) {
int count = 0;
for (int i = startingIndex; i < array.length; i++) {
String matchValue = val + array[i];
if (matchValue.length() <= input.length()) {
// if value matches then count + 1
if (matchValue.equals(input)) {
count++;
System.out.println("match Found---->" + count); //ommit this sysout , its only for testing.
return count;
} else if (input.startsWith(matchValue)) { // checking whether the input is starting with the new value
// search further combos
count += getCombo(array, 0, matchValue, input);
}
}
}
return count;
}
In main Method
String[] arr = substrings.toArray(new String[0]);
int count = 0;
for (int i = 0; i < arr.length; i++) {
System.out.println("index----?> " + i);
//adding this condition for single inputs i.e "0","010";
if(arr[i].equals(input))
count++;
else
count = count + getCombo(arr, 0, arr[i], input);
}
System.out.println("Final count : " + count);
My test results :
input : 00100
Final count 5
input : 000
Final count 3
I have to create a code that can find the longest palindrome contained inside sentences. (eg. Some people like cake but I prefer pie; the longest palindrome is i prefer pi). The problem is that upon running the code it doesn't return the palindrome. I'm not sure what the problem is but if anyone can figure it out I'd appreciate you letting me know. Thanks!
Code is below...
public class Recursion6 {
static String recursion(String word, int currentLength, int x, String substring) {
String reverse =new StringBuffer(word).reverse().toString();
if(word.length() == 1 ){
return substring;
}
if(word.charAt(0) != word.charAt(x)) {
if(x == word.length() - 1) {
recursion(word.substring(1), currentLength, 1, substring);
}
x++;
recursion(word, currentLength, x, substring);
} else {
if(word.substring(0, x + 1).equalsIgnoreCase(reverse.substring(word.length() - (x+1), word.length()))) {
if(word.substring(0, x).length() > currentLength) {
currentLength = word.substring(0, x + 1).length();
substring = word.substring(0, x + 1);
}
recursion(word.substring(1), currentLength, 1, substring);
}
recursion(word.substring(1), currentLength, 1, substring);
}
return substring;
}
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
System.out.println("Enter a Sentence:");
String word=sc.nextLine();
System.out.println("The Palendrome is "+recursion(word.replaceAll(" ", ""), 1, 1, null));
sc.close();
}
}
You ignore the return value.
For example:
recursion(word, currentLength, x, substring);
There's a return value, all right. You just do nothing with it for the recursive calls. What gets returned from the outermost call is just the input to the outermost call, which is a null string.
You probably need to review how recursive activation works. The 'return' statement only returns from the current level, it does not empty the entire call stack.
Here's another approach.
First, just get all the substrings on the stack. That's what the first two recursive calls do.
Then just check each substring as the stack unwinds to see if it is a palindrome.
the private method does the actual check. It simply compares characters at both ends towards the middle.
Final output may contain white space and/or punctuation characters.
public static void main(String[] args) {
String s =
"Some people like radar, others " +
"like a man, a plan, a canal, panama!, but " +
"my favorite is 'I prefer pie'";
String res = findLongestPalindrome(s,0,s.length());
System.out.printf("'%s'%n", res);
prints
' a man, a plan, a canal, panama!, '
The method takes the string, and starting and ending indices.
public static String findLongestPalindrome(String s, int start, int end) {
String save = "";
if (end - start > 0) {
save = findLongestPalindrome(s, start + 1, end);
} else if (end > 1) {
save = findLongestPalindrome(s, 0, end - 1);
}
String temp;
if (isPalindrome(temp = s.substring(start, end))) {
if (temp.length() > save.length()) {
save = temp;
}
}
return save;
}
// iterates from the ends toward the middle.
private static boolean isPalindrome(String s) {
s = s.replaceAll("\\W+", "").toLowerCase();
int end = s.length();
for (int i = 0; i < s.length() >> 1; i++) {
if (s.charAt(i) != s.charAt(--end)) {
return false; // fast fail if any two are not equal
}
}
return true;
}
For an school assignment, we are implementing suffixarray, with the methods of building it and finding the longest common prefix. I manage to build and sort the suffix array quite easily but struggle with the LCP.
I am trying to find the longest common prefix of a pattern string P in another string T, using one singular binary search. The algorithm should return the index of where the longest common prefix begins.
Examples:
If the pattern string P is "racad" and the string T is "abracadabra", the longest common prefix should be "racad", beginning at index 2.
Likewise, if the the pattern string is P "rax" then the longest common prefix should be "ra", beginning at index 2 or 9.
I´ve come quite far but the algorithm is not returning the right value. Here´s my code:
public int compareWithSuffix(int i, String pattern) {
int c = 0;
int j = 0;
while (j < pattern.length() && c == 0) {
if (i + j <= text.length()) {
c = pattern.charAt(0 + j) - text.charAt(i + j);
} else {
c = 1;
}
j++;
}
return c;
}
public int binarySearch(String pattern) {
int left = 0;
int right = text.length() - 1;
int mid, c = 0;
while (c != 0 && left <= right) {
mid = left + (right - left) / 2;
c = compareWithSuffix(mid, pattern);
if (c < 0) {
right = mid - 1;
} else if (c > 0) {
left = mid + 1;
} else if (c == 0) {
return mid;
}
}
return left;
}
I run it with this main-method:
public static void main(String[] args) {
String word = "abracadabra";
String prefix1 = "rax";
String prefix2 = "racad";
SuffixArray s = new SuffixArray(word);
System.out.println("Longest common prefix of: " + "'" + prefix1 + "'" + " in " + "'" + word + "'" + " begins at index: " + s.binarySearch(prefix1));
System.out.println("Longest common prefix of: " + "'" + prefix2 + "'" + " in " + "'" + word + "'" + " begins at index: " + s.binarySearch(prefix2));
}
The output is always whatever value I initialize the local variable left with.
The search algorithm must do a singular binary search. I´ve tried searching other stackoverflow-questions and other web-sources but have not found anything helpfull.
Anyone who can see any errors in my code?
I haven't looked deeply enough to know if this is the only problem in your code, but this immediately jumps out as an explanation for "The output is always whatever value I initialize the local variable left with":
int mid, c = 0;
while (c != 0 && left <= right) {
You set c to zero, and then immediately check if it's not equal to zero. Of course, it's not not equal to zero, so the loop condition is immediately false, thus the loop body is never run. Hence, you will return the initial value of left.
It's not obvious why you are checking c at all. In the only situation where c becomes zero inside the loop, you immediately return. So just change your loop guard to:
while (left <= right) {
(and move the declaration of c inside the loop).
You could easily have found this by stepping through the code with a debugger. I heartily recommend learning how to use one.
first point: analyzing the examples you gave, it appears that you are not interested in the longest common prefix, but in the longest common substring. A prefix always starts with the first letter of the word - https://en.wikipedia.org/wiki/Prefix
second point: perhaps are you interested in finding the longest common substring of a set of words, or just two words?
public class Main3 {
/*
same functionallity as compareWithSuffix, but i think this name
is more suggestive; also, no need to pass i (the starting index) as a
parameter; i will later apply substring(i) to text
*/
public String longestCommonPrefix(String text, String pattern)
{
String commonPrefix="";
for(int j=0; j<text.length() & j<pattern.length(); j++)
{
if(text.charAt(j)==pattern.charAt(j))
{
commonPrefix=commonPrefix+text.charAt(j);
}
else
{
break;
}
}
return commonPrefix;
//for params "abc", "abd", output will be "ab"
}
public String longestCommonSequence(String s1, String s2)
{
/*
take for example "xab" and "yab";in order to find the common
sequence 'ab", we need to chop both x and y; for this reason
i apply substring to both strings, cutting progressivelly their first letters
*/
String longestCommonSequence="";
for(int i=0;i<=s1.length()-1;i++)
{
for(int j=0;j<=s2.length()-1;j++)
{
String s1_temp=s1.substring(i);
String s2_temp=s2.substring(j);
String commonSequence_temp=longestCommonPrefix(s1_temp, s2_temp);
if(commonSequence_temp.length()>longestCommonSequence.length())
longestCommonSequence=commonSequence_temp;
}
}
return longestCommonSequence;
}
public static void main(String args[])
{
Main3 m = new Main3();
String common = m.longestCommonSequence("111abcd2222", "33abcd444");
System.out.println(common);//"abcd"
}
}
I am providing here a different answer, because the approach is totally different, and leads to a generalized solution. (find the common substring of an entire list of strings)
For each word, i build all its possible substrings. A substring is determined by its start and end index. If the length of a word is L, the start index can be: 0, 1, 2,... L-1; if the starting index is 0, the end index can take values from 1 to L-1, thus L-1 values; if the starting index is 1, there are L-2 possible values for the end index. Thus, a word of length L has (L-1) +(L-2) + ... +1 = L*(L-1)/2 substrings. This gives square complexity in regard to L, but that's not an issue, because words rarely exceed 15 letters in length. If the string is not a word, but a text paragraph, then we have a problem with the square complexity.
Next, after i have build the set of substrings for every word, i build the intersection of these sets. The main idea is that a common substring of more words is, in the first place, a substring inside every such word, and, moreover, a substring that is encountered in all of these words. This lead to the idea of building the set of substrings for every word, and then do the intersection.
After we have found all the common substrings, just iterate and keep the longest one
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Main4 {
HashSet<String> allSubstrings(String input)
{
HashSet<String> result = new HashSet<String>();
for(int i=0;i<=input.length()-1;i++)
for(int j=i+1;j<=input.length();j++)
result.add(input.substring(i,j));
return result;
}
public HashSet<String> allCommonSubstrings(ArrayList<String> listOfStrings)
{
ArrayList<HashSet<String>> listOfSetsOfSubstrings =new ArrayList<HashSet<String>>();
//for each string in the list, build the set of all its possible substrings
for(int i=0;i<listOfStrings.size();i++)
{
String currentString = listOfStrings.get(i);
HashSet<String> allSubstrings = allSubstrings(currentString);
listOfSetsOfSubstrings.add(allSubstrings);
}
//find the intersection of all the sets of substrings
HashSet<String> intersection = new HashSet<String>(listOfSetsOfSubstrings.get(0));
for(int i=0;i<listOfSetsOfSubstrings.size();i++)
{
HashSet<String> currentSet=listOfSetsOfSubstrings.get(i);
intersection.retainAll(currentSet);
//retainAll does the set intersection. see: https://stackoverflow.com/questions/8882097/how-to-calculate-the-intersection-of-two-sets
}
return intersection;
}
public String longestCommonSubstring(HashSet<String> setOfSubstrings)
{
if(setOfSubstrings.size()==0)
return null;//if there are no common substrings, then there is no longest common substrings
String result="";
Iterator<String> it = setOfSubstrings.iterator();
while(it.hasNext())
{
String current = it.next();
if(current.length()>result.length())
result=current;
}
return result;
}
public static void main(String[] args)
{
Main4 m = new Main4();
ArrayList<String> list=new ArrayList<String>();
list.add("bbbaaddd1");
list.add("bbbaaccc1");
list.add("dddaaccc1");
HashSet<String> hset = m.allCommonSubstrings(list);
Iterator<String> it = hset.iterator();
System.out.println("all coommon substrings:");
while(it.hasNext())
{
System.out.println(it.next());
}
System.out.println("longest common substring:");
String lcs=m.longestCommonSubstring(hset);
System.out.println(lcs);
}
}
output:
all coommon substrings:
aa
a
1
longest common substring:
aa
I need to concatenate two string in another one without their intersection (in terms of last/first words).
In example:
"Some little d" + "little dogs are so pretty" = "Some little dogs are so pretty"
"I love you" + "love" = "I love youlove"
What is the most efficient way to do this in Java?
Here we go - if the first doesn't even contain the first letter of the second string, just return the concatenation. Otherwise, go from longest to shortest on the second string, seeing if the first ends with it. If so, return the non-overlapping parts, otherwise try one letter shorter.
public static String docat(String f, String s) {
if (!f.contains(s.substring(0,1)))
return f + s;
int idx = s.length();
try {
while (!f.endsWith(s.substring(0, idx--))) ;
} catch (Exception e) { }
return f + s.substring(idx + 1);
}
docat("Some little d", "little dogs are so pretty");
-> "Some little dogs are so pretty"
docat("Hello World", "World")
-> "Hello World"
docat("Hello", "World")
-> "HelloWorld"
EDIT: In response to the comment, here is a method using arrays. I don't know how to stress test these properly, but none of them took over 1ms in my testing.
public static String docat2(String first, String second) {
char[] f = first.toCharArray();
char[] s = second.toCharArray();
if (!first.contains("" + s[0]))
return first + second;
int idx = 0;
try {
while (!matches(f, s, idx)) idx++;
} catch (Exception e) { }
return first.substring(0, idx) + second;
}
private static boolean matches(char[] f, char[] s, int idx) {
for (int i = idx; i <= f.length; i++) {
if (f[i] != s[i - idx])
return false;
}
return true;
}
Easiest: iterate over the first string taking suffixes ("Some little d", "ome little d", "me little d"...) and test the second string with .startsWith. When you find a match, concatenate the prefix of the first string with the second string.
Here's the code:
String overlappingConcat(String a, String b) {
int i;
int l = a.length();
for (i = 0; i < l; i++) {
if (b.startsWith(a.substring(i))) {
return a.substring(0, i) + b;
}
}
return a + b;
}
The biggest efficiency problem here is the creation of new strings at substring. Implementing a custom stringMatchFrom(a, b, aOffset) should improve it, and is trivial.
You can avoid creating unnecessary substrings with the regionMatches() method.
public static String intersecting_concatenate(String a, String b) {
// Concatenate two strings, but if there is overlap at the intersection,
// include the intersection/overlap only once.
// find length of maximum possible match
int len_a = a.length();
int len_b = b.length();
int max_match = (len_a > len_b) ? len_b : len_a;
// search down from maximum match size, to get longest possible intersection
for (int size=max_match; size>0; size--) {
if (a.regionMatches(len_a - size, b, 0, size)) {
return a + b.substring(size, len_b);
}
}
// Didn't find any intersection. Fall back to straight concatenation.
return a + b;
}
isBlank(CharSequence), join(T...) and left(String, int) are methods from Apache Commons.
public static String joinOverlap(String s1, String s2) {
if(isBlank(s1) || isBlank(s2)) { //empty or null input -> normal join
return join(s1, s2);
}
int start = Math.max(0, s1.length() - s2.length());
for(int i = start; i < s1.length(); i++) { //this loop is for start point
for(int j = i; s1.charAt(j) == s2.charAt(j-i); j++) { //iterate until mismatch
if(j == s1.length() - 1) { //was it s1's last char?
return join(left(s1, i), s2);
}
}
}
return join(s1, s2); //no overlapping; do normal join
}
Create a suffix tree of the first String, then traverse the tree from the root taking characters from the beginning of the second String and keeping track of the longest suffix found.
This should be the longest suffix of the first String that is a prefix of the second String. Remove the suffix, then append the second String.
This should all be possible in linear time instead of the quadratic time required to loop through and compare all suffixes.
The following code seems to work for the first example. I did not test it extensively, but you get the point. It basically searches for all occurrences of the first char of the secondString in the firstString since these are the only possible places where overlap can occur. Then it checks whether the rest of the first string is the start of the second string. Probably the code contains some errors when no overlap is found, ... but it was more an illustration of my answer
String firstString = "Some little d";
String secondString = "little dogs are so pretty";
String startChar = secondString.substring( 0, 1 );
int index = Math.max( 0, firstString.length() - secondString.length() );
int length = firstString.length();
int searchedIndex = -1;
while ( searchedIndex == -1 && ( index = firstString.indexOf( startChar, index ) )!= -1 ){
if ( secondString.startsWith( firstString.substring( index, length ) ) ){
searchedIndex = index;
}
}
String result = firstString.substring( 0, searchedIndex ) + secondString;
I was asked this question in a phone interview for summer internship, and tried to come up with a n*m complexity solution (although it wasn't accurate too) in Java.
I have a function that takes 2 strings, suppose "common" and "cmn". It should return True based on the fact that 'c', 'm', 'n' are occurring in the same order in "common". But if the arguments were "common" and "omn", it would return False because even though they are occurring in the same order, but 'm' is also appearing after 'o' (which fails the pattern match condition)
I have worked over it using Hashmaps, and Ascii arrays, but didn't get a convincing solution yet! From what I have read till now, can it be related to Boyer-Moore, or Levenshtein Distance algorithms?
Hoping for respite at stackoverflow! :)
Edit: Some of the answers talk about reducing the word length, or creating a hashset. But per my understanding, this question cannot be done with hashsets because occurrence/repetition of each character in first string has its own significance. PASS conditions- "con", "cmn", "cm", "cn", "mn", "on", "co". FAIL conditions that may seem otherwise- "com", "omn", "mon", "om". These are FALSE/FAIL because "o" is occurring before as well as after "m". Another example- "google", "ole" would PASS, but "google", "gol" would fail because "o" is also appearing before "g"!
I think it's quite simple. Run through the pattern and fore every character get the index of it's last occurence in the string. The index must always increase, otherwise return false.
So in pseudocode:
index = -1
foreach c in pattern
checkindex = string.lastIndexOf(c)
if checkindex == -1 //not found
return false
if checkindex < index
return false
if string.firstIndexOf(c) < index //characters in the wrong order
return false
index = checkindex
return true
Edit: you could further improve the code by passing index as the starting index to the lastIndexOf method. Then you would't have to compare checkindex with index and the algorithm would be faster.
Updated: Fixed a bug in the algorithm. Additional condition added to consider the order of the letters in the pattern.
An excellent question and couple of hours of research and I think I have found the solution. First of all let me try explaining the question in a different approach.
Requirement:
Lets consider the same example 'common' (mainString) and 'cmn'(subString). First we need to be clear that any characters can repeat within the mainString and also the subString and since its pattern that we are concentrating on, the index of the character play a great role to. So we need to know:
Index of the character (least and highest)
Lets keep this on hold and go ahead and check the patterns a bit more. For the word common, we need to find whether the particular pattern cmn is present or not. The different patters possible with common are :- (Precedence apply )
c -> o
c -> m
c -> n
o -> m
o -> o
o -> n
m -> m
m -> o
m -> n
o -> n
At any moment of time this precedence and comparison must be valid. Since the precedence plays a huge role, we need to have the index of each unique character Instead of storing the different patterns.
Solution
First part of the solution is to create a Hash Table with the following criteria :-
Create a Hash Table with the key as each character of the mainString
Each entry for a unique key in the Hash Table will store two indices i.e lowerIndex and higherIndex
Loop through the mainString and for every new character, update a new entry of lowerIndex into the Hash with the current index of the character in mainString.
If Collision occurs, update the current index with higherIndex entry, do this until the end of String
Second and main part of pattern matching :-
Set Flag as False
Loop through the subString and for
every character as the key, retreive
the details from the Hash.
Do the same for the very next character.
Just before loop increment, verify two conditions
If highestIndex(current character) > highestIndex(next character) Then
Pattern Fails, Flag <- False, Terminate Loop
// This condition is applicable for almost all the cases for pattern matching
Else If lowestIndex(current character) > lowestIndex(next character) Then
Pattern Fails, Flag <- False, Terminate Loop
// This case is explicitly for cases in which patterns like 'mon' appear
Display the Flag
N.B : Since I am not so versatile in Java, I did not submit the code. But some one can try implementing my idea
I had myself done this question in an inefficient manner, but it does give accurate result! I would appreciate if anyone can make out an an efficient code/algorithm from this!
Create a function "Check" which takes 2 strings as arguments. Check each character of string 2 in string 1. The order of appearance of each character of s2 should be verified as true in S1.
Take character 0 from string p and traverse through the string s to find its index of first occurrence.
Traverse through the filled ascii array to find any value more than the index of first occurrence.
Traverse further to find the last occurrence, and update the ascii array
Take character 1 from string p and traverse through the string s to find the index of first occurence in string s
Traverse through the filled ascii array to find any value more than the index of first occurrence. if found, return False.
Traverse further to find the last occurrence, and update the ascii array
As can be observed, this is a bruteforce method...I guess O(N^3)
public class Interview
{
public static void main(String[] args)
{
if (check("google", "oge"))
System.out.println("yes");
else System.out.println("sorry!");
}
public static boolean check (String s, String p)
{
int[] asciiArr = new int[256];
for(int pIndex=0; pIndex<p.length(); pIndex++) //Loop1 inside p
{
for(int sIndex=0; sIndex<s.length(); sIndex++) //Loop2 inside s
{
if(p.charAt(pIndex) == s.charAt(sIndex))
{
asciiArr[s.charAt(sIndex)] = sIndex; //adding char from s to its Ascii value
for(int ascIndex=0; ascIndex<256; ) //Loop 3 for Ascii Array
{
if(asciiArr[ascIndex]>sIndex) //condition to check repetition
return false;
else ascIndex++;
}
}
}
}
return true;
}
}
Isn't it doable in O(n log n)?
Step 1, reduce the string by eliminating all characters that appear to the right. Strictly speaking you only need to eliminate characters if they appear in the string you're checking.
/** Reduces the maximal subsequence of characters in container that contains no
* character from container that appears to the left of the same character in
* container. E.g. "common" -> "cmon", and "whirlygig" -> "whrlyig".
*/
static String reduceContainer(String container) {
SparseVector charsToRight = new SparseVector(); // Like a Bitfield but sparse.
StringBuilder reduced = new StringBuilder();
for (int i = container.length(); --i >= 0;) {
char ch = container.charAt(i);
if (charsToRight.add(ch)) {
reduced.append(ch);
}
}
return reduced.reverse().toString();
}
Step 2, check containment.
static boolean containsInOrder(String container, String containee) {
int containerIdx = 0, containeeIdx = 0;
int containerLen = container.length(), containeeLen == containee.length();
while (containerIdx < containerLen && containeeIdx < containeeLen) {
// Could loop over codepoints instead of code-units, but you get the point...
if (container.charAt(containerIdx) == containee.charAt(containeeIdx)) {
++containeeIdx;
}
++containerIdx;
}
return containeeIdx == containeeLen;
}
And to answer your second question, no, Levenshtein distance won't help you since it has the property that if you swap the arguments the output is the same, but the algo you want does not.
public class StringPattern {
public static void main(String[] args) {
String inputContainer = "common";
String inputContainees[] = { "cmn", "omn" };
for (String containee : inputContainees)
System.out.println(inputContainer + " " + containee + " "
+ containsCommonCharsInOrder(inputContainer, containee));
}
static boolean containsCommonCharsInOrder(String container, String containee) {
Set<Character> containerSet = new LinkedHashSet<Character>() {
// To rearrange the order
#Override
public boolean add(Character arg0) {
if (this.contains(arg0))
this.remove(arg0);
return super.add(arg0);
}
};
addAllPrimitiveCharsToSet(containerSet, container.toCharArray());
Set<Character> containeeSet = new LinkedHashSet<Character>();
addAllPrimitiveCharsToSet(containeeSet, containee.toCharArray());
// retains the common chars in order
containerSet.retainAll(containeeSet);
return containerSet.toString().equals(containeeSet.toString());
}
static void addAllPrimitiveCharsToSet(Set<Character> set, char[] arr) {
for (char ch : arr)
set.add(ch);
}
}
Output:
common cmn true
common omn false
I would consider this as one of the worst pieces of code I have ever written or one of the worst code examples in stackoverflow...but guess what...all your conditions are met!
No algorithm could really fit the need, so I just used bruteforce...test it out...
And I could just care less for space and time complexity...my aim was first to try and solve it...and maybe improve it later!
public class SubString {
public static void main(String[] args) {
SubString ss = new SubString();
String[] trueconditions = {"con", "cmn", "cm", "cn", "mn", "on", "co" };
String[] falseconditions = {"com", "omn", "mon", "om"};
System.out.println("True Conditions : ");
for (String str : trueconditions) {
System.out.println("SubString? : " + str + " : " + ss.test("common", str));
}
System.out.println("False Conditions : ");
for (String str : falseconditions) {
System.out.println("SubString? : " + str + " : " + ss.test("common", str));
}
System.out.println("SubString? : ole : " + ss.test("google", "ole"));
System.out.println("SubString? : gol : " + ss.test("google", "gol"));
}
public boolean test(String original, String match) {
char[] original_array = original.toCharArray();
char[] match_array = match.toCharArray();
int[] value = new int[match_array.length];
int index = 0;
for (int i = 0; i < match_array.length; i++) {
for (int j = index; j < original_array.length; j++) {
if (original_array[j] != original_array[j == 0 ? j : j-1] && contains(match.substring(0, i), original_array[j])) {
value[i] = 2;
} else {
if (match_array[i] == original_array[j]) {
if (value[i] == 0) {
if (contains(original.substring(0, j == 0 ? j : j-1), match_array[i])) {
value[i] = 2;
} else {
value[i] = 1;
}
}
index = j + 1;
}
}
}
}
for (int b : value) {
if (b != 1) {
return false;
}
}
return true;
}
public boolean contains(String subStr, char ch) {
for (char c : subStr.toCharArray()) {
if (ch == c) {
return true;
}
}
return false;
}
}
-IvarD
I think this one is not a test of your computer science fundamentals, more what you would practically do within the Java programming environment.
You could construct a regular expression out of the second argument, i.e ...
omn -> o.*m[^o]*n
... and then test candidate string against this by either using String.matches(...) or using the Pattern class.
In generic form, the construction of the RegExp should be along the following lines.
exp -> in[0].* + for each x : 2 -> in.lenght { (in[x-1] +
[^in[x-2]]* + in[x]) }
for example:
demmn -> d.*e[^d]*m[^e]*m[^m]*n
I tried it myself in a different way. Just sharing my solution.
public class PatternMatch {
public static boolean matchPattern(String str, String pat) {
int slen = str.length();
int plen = pat.length();
int prevInd = -1, curInd;
int count = 0;
for (int i = 0; i < slen; i++) {
curInd = pat.indexOf(str.charAt(i));
if (curInd != -1) {
if(prevInd == curInd)
continue;
else if(curInd == (prevInd+1))
count++;
else if(curInd == 0)
count = 1;
else count = 0;
prevInd = curInd;
}
if(count == plen)
return true;
}
return false;
}
public static void main(String[] args) {
boolean r = matchPattern("common", "on");
System.out.println(r);
}
}