Related
Lets say I have String word = "hello12".
I need to have all possible combinations of special characters instead of numbers (characters I get when use shift+number). So, the result I want to get is hello12, hello!2, hello1#, hello!#.
What I did is created switch with all cases (1 = '!', 2 = '#'...) but I can't get how to code all the combinations. All I could code is change all the numbers with special symbols (code is below)
char[] passwordInCharArray;
for(int i=0; i<passwordList.length; i++){
for(int j = 0; j<passwordList[i].length(); j++){
if(Character.isDigit((passwordList[i].charAt(j)))){
passwordInCharArray = passwordList[i].toCharArray();
passwordInCharArray[j] = getSpecialSymbol(passwordList[i].charAt(j));
passwordList[i]=String.valueOf(passwordInCharArray);
}
}
}
Theory
Combinatory is often easier to express with recursive methods (methods that call themselves).
I think that the algorithm is more understandable with an example so let's take String word = hello12.
We will iterate on each character until a digit is found. The first one is 1. At this point, we can imagine the word been split in two by a virtual cursor:
hello is on the left side. We know that it won't change.
12 is on the right side. Each character is likely to be a digit and thus to change.
To retrieve all the possible combinations, we want to:
Keep the first part of the word
Compute all the possible combinations of the second part of the word
Append each of these combinations to the first part of the word
The following tree represents what we want to compute (the root is the first part of the word, each branch represent a combination)
hello
├───1
│ ├───2 (-> hello12)
│ └───# (-> hello1#)
└───!
├───2 (-> hello!2)
└───# (-> hello!#)
You want to write an algorithm that gathers all the branches of this tree.
Java Code
/!\ I advise you to try to implement what I described above before taking a look at the code: that's how we improve ourselves!
Here is the corresponding Java code:
public static void main(String[] args) {
Set<String> combinations = combinate("hello12");
combinations.forEach(System.out::println);
}
public static Set<String> combinate(String word) {
// Will hold all the combinations of word
Set<String> combinations = new HashSet<String>();
// The word is a combination (could be ignored if empty, though)
combinations.add(word);
// Iterate on each word's characters
for (int i = 0; i < word.toCharArray().length; i++) {
char character = word.toCharArray()[i];
// If the character should be replaced...
if (Character.isDigit(character)) {
// ... we split the word in two at the character's position & pay attention not be exceed word's length
String firstWordPart = word.substring(0, i);
boolean isWordEnd = i + 1 >= word.length();
String secondWordPart = isWordEnd ? "" : word.substring(i + 1);
// Here is the trick: we compute all combinations of the second word part...
Set<String> allCombinationsOfSecondPart = combinate(secondWordPart);
// ... and we append each of them to the first word part one by one
for (String string : allCombinationsOfSecondPart) {
String combination = firstWordPart + getSpecialSymbol(character) + string;
combinations.add(combination);
}
}
}
return combinations;
}
Please leave a comment if you want me to explain the algorithm further.
Building on the code from: Generate All Possible Combinations - Java, I've come up with this implementation that does what you need. It will find the index of all digits in your string and then generate all possibilities in which they can be replaced with the special characters.
import java.util.*;
public class Comb {
public static List<String> combinations(String pass) {
String replace = ")!##$%^&*(";
char[] password = pass.toCharArray();
List<Integer> index = new ArrayList<Integer>();
List<String> results = new ArrayList<String>();
results.add(pass);
//find all digits
for (int i = 0; i < password.length; i++) {
if (Character.isDigit(password[i])) {
index.add(i);
}
}
//generate combinations
int N = (int) Math.pow(2d, Double.valueOf(index.size()));
for (int i = 1; i < N; i++) {
String code = Integer.toBinaryString(N | i).substring(1);
char[] p = Arrays.copyOf(password, password.length);
//replace the digits with special chars
for (int j = 0; j < index.size(); j++) {
if (code.charAt(j) == '1') {
p[index.get(j)] = replace.charAt(p[index.get(j)] - '0');
}
}
results.add(String.valueOf(p));
}
return results;
}
public static void main(String... args) {
System.out.println(combinations("hello12"));
}
}
Given is a String word and a String array book that contains some strings. The program should give out the number of possibilities to create word only using elements in book. An element can be used as many times as we want and the program must terminate in under 6 seconds.
For example, input:
String word = "stackoverflow";
String[] book = new String[9];
book[0] = "st";
book[1] = "ck";
book[2] = "CAG";
book[3] = "low";
book[4] = "TC";
book[5] = "rf";
book[6] = "ove";
book[7] = "a";
book[8] = "sta";
The output should be 2, since we can create "stackoverflow" in two ways:
1: "st" + "a" + "ck" + "ove" + "rf" + "low"
2: "sta" + "ck" + "ove" + "rf" + "low"
My implementation of the program only terminates in the required time if word is relatively small (<15 characters). However, as I mentioned before, the running time limit for the program is 6 seconds and it should be able to handle very large word strings (>1000 characters). Here is an example of a large input.
Here is my code:
1) the actual method:
input: a String word and a String[] book
output: the number of ways word can be written only using strings in book
public static int optimal(String word, String[] book){
int count = 0;
List<List<String>> allCombinations = allSubstrings(word);
List<String> empty = new ArrayList<>();
List<String> wordList = Arrays.asList(book);
for (int i = 0; i < allCombinations.size(); i++) {
allCombinations.get(i).retainAll(wordList);
if (!sumUp(allCombinations.get(i), word)) {
allCombinations.remove(i);
allCombinations.add(i, empty);
}
else count++;
}
return count;
}
2) allSubstrings():
input: a String input
output: A list of lists, each containing a combination of substrings that add up to input
static List<List<String>> allSubstrings(String input) {
if (input.length() == 1) return Collections.singletonList(Collections.singletonList(input));
List<List<String>> result = new ArrayList<>();
for (List<String> temp : allSubstrings(input.substring(1))) {
List<String> firstList = new ArrayList<>(temp);
firstList.set(0, input.charAt(0) + firstList.get(0));
if (input.startsWith(firstList.get(0), 0)) result.add(firstList);
List<String> l = new ArrayList<>(temp);
l.add(0, input.substring(0, 1));
if (input.startsWith(l.get(0), 0)) result.add(l);
}
return result;
}
3.) sumup():
input: A String list input and a String expected
output: true if the elements in input add up to expected
public static boolean sumUp (List<String> input, String expected) {
String x = "";
for (int i = 0; i < input.size(); i++) {
x = x + input.get(i);
}
if (expected.equals(x)) return true;
return false;
}
I've figured out what I was doing wrong in my previous answer: I wasn't using memoization, so I was redoing an awful lot of unnecessary work.
Consider a book array {"a", "aa", "aaa"}, and a target word "aaa". There are four ways to construct this target:
"a" + "a" + "a"
"aa" + "a"
"a" + "aa"
"aaa"
My previous attempt would have walk through all four, separately. But instead, one can observe that:
There is 1 way to construct "a"
You can construct "aa" in 2 ways, either "a" + "a" or using "aa" directly.
You can construct "aaa" either by using "aaa" directly (1 way); or "aa" + "a" (2 ways, since there are 2 ways to construct "aa"); or "a" + "aa" (1 way).
Note that the third step here only adds a single additional string to a previously-constructed string, for which we know the number of ways it can be constructed.
This suggests that if we count the number of ways in which a prefix of word can be constructed, we can use that to trivially calculate the number of ways a longer prefix by adding just one more string from book.
I defined a simple trie class, so you can quickly look up prefixes of the book words that match at any given position in word:
class TrieNode {
boolean word;
Map<Character, TrieNode> children = new HashMap<>();
void add(String s, int i) {
if (i == s.length()) {
word = true;
} else {
children.computeIfAbsent(s.charAt(i), k -> new TrieNode()).add(s, i + 1);
}
}
}
For each letter in s, this creates an instance of TrieNode, and stores the TrieNode for the subsequent characters etc.
static long method(String word, String[] book) {
// Construct a trie from all the words in book.
TrieNode t = new TrieNode();
for (String b : book) {
t.add(b, 0);
}
// Construct an array to memoize the number of ways to construct
// prefixes of a given length: result[i] is the number of ways to
// construct a prefix of length i.
long[] result = new long[word.length() + 1];
// There is only 1 way to construct a prefix of length zero.
result[0] = 1;
for (int m = 0; m < word.length(); ++m) {
if (result[m] == 0) {
// If there are no ways to construct a prefix of this length,
// then just skip it.
continue;
}
// Walk the trie, taking the branch which matches the character
// of word at position (n + m).
TrieNode tt = t;
for (int n = 0; tt != null && n + m <= word.length(); ++n) {
if (tt.word) {
// We have reached the end of a word: we can reach a prefix
// of length (n + m) from a prefix of length (m).
// Increment the number of ways to reach (n+m) by the number
// of ways to reach (m).
// (Increment, because there may be other ways).
result[n + m] += result[m];
if (n + m == word.length()) {
break;
}
}
tt = tt.children.get(word.charAt(n + m));
}
}
// The number of ways to reach a prefix of length (word.length())
// is now stored in the last element of the array.
return result[word.length()];
}
For the very long input given by OP, this gives output:
$ time java Ideone
2217093120
real 0m0.126s
user 0m0.146s
sys 0m0.036s
Quite a bit faster than the required 6 seconds - and this includes JVM startup time too.
Edit: in fact, the trie isn't necessary. You can simply replace the "Walk the trie" loop with:
for (String b : book) {
if (word.regionMatches(m, b, 0, b.length())) {
result[m + b.length()] += result[m];
}
}
and it performs slower, but still way faster than 6s:
2217093120
real 0m0.173s
user 0m0.226s
sys 0m0.033s
A few observations:
x = x + input.get(i);
As you are looping, using String+ isn't a good idea. Use a StringBuilder and append to that within the loop, and in the end return builder.toString(). Or you follow the idea from Andy. There is no need to merge strings, you already know the target word. See below.
Then: List implies that adding/removing elements might be costly. So see if you can get rid of that part, and if it would be possible to use maps, sets instead.
Finally: the real point would be to look into your algorithm. I would try to work "backwards". Meaning: first identify those array elements that actually occur in your target word. You can ignore all others right from start.
Then: look at all array entries that **start*+ your search word. In your example you can notice that there are just two array elements that fit. And then work your way from there.
My first observation would be that you don't actually need to build anything: you know what string you are trying to construct (e.g. stackoverflow), so all you really need to keep track of is how much of that string you have matched so far. Call this m.
Next, having matched m characters, provided m < word.length(), you need to choose a next string from book which matches the portion of word from m to m + nextString.length().
You could do this by checking each string in turn:
if (word.matches(m, nextString, 0, nextString.length()) { ...}
But you can do better, by determining strings that can't match in advance: the next string you append will have the following properties:
word.charAt(m) == nextString.charAt(0) (the next characters match)
m + nextString.length() <= word.length() (adding the next string shouldn't make the constructed string longer than word)
So, you can cut down the potential words from book that you might check by constructing a map of letters to words that start with that (point 1); and if you store the words with the same starting letter in increasing length order, you can stop checking that letter as soon as the length gets too big (point 2).
You can construct a map once and reuse:
Map<Character, List<String>> prefixMap =
Arrays.asList(book).stream()
.collect(groupingBy(
s -> s.charAt(0),
collectingAndThen(
toList(),
ss -> {
ss.sort(comparingInt(String::length));
return ss;
})));
You can count the number of ways recursively, without constructing any additional objects (*):
int method(String word, String[] book) {
return method(word, 0, /* construct map as above */);
}
int method(String word, int m, Map<Character, List<String>> prefixMap) {
if (m == word.length()) {
return 1;
}
int result = 0;
for (String nextString : prefixMap.getOrDefault(word.charAt(m), emptyList())) {
if (m + nextString.length() > word.length()) {
break;
}
// Start at m+1, because you already know they match at m.
if (word.regionMatches(m + 1, nextString, 1, nextString.length()-1)) {
// This is a potential match!
// Make a recursive call.
result += method(word, m + nextString.length(), prefixMap);
}
}
return result;
}
(*) This may construct new instances of Character, because of the boxing of the word.charAt(m): cached instances are guaranteed to be used for chars in the range 0-127 only. There are ways to work around this, but they would only clutter the code.
I think you are already doing a pretty good job at optimizing your application. In addition to the answer by GhostCat here are a few suggestions of my own:
public static int optimal(String word, String[] book){
int count = 0;
List<List<String>> allCombinations = allSubstrings(word);
List<String> wordList = Arrays.asList(book);
for (int i = 0; i < allCombinations.size(); i++)
{
/*
* allCombinations.get(i).retainAll(wordList);
*
* There is no need to retrieve the list element
* twice, just set it in a local variable
*/
java.util.List<String> combination = allCombinations.get(i);
combination.retainAll(wordList);
/*
* Since we are only interested in the count here
* there is no need to remove and add list elements
*/
if (sumUp(combination, word))
{
/*allCombinations.remove(i);
allCombinations.add(i, empty);*/
count++;
}
/*else count++;*/
}
return count;
}
public static boolean sumUp (List<String> input, String expected) {
String x = "";
for (int i = 0; i < input.size(); i++) {
x = x + input.get(i);
}
// No need for if block here, just return comparison result
/*if (expected.equals(x)) return true;
return false;*/
return expected.equals(x);
}
And since you are interested in seeing the execution time of your method I would recommend implementing a benchmarking system of some sort. Here is a quick mock-up:
private static long benchmarkOptima(int cycles, String word, String[] book) {
long totalTime = 0;
for (int i = 0; i < cycles; i++)
{
long startTime = System.currentTimeMillis();
int a = optimal(word, book);
long executionTime = System.currentTimeMillis() - startTime;
totalTime += executionTime;
}
return totalTime / cycles;
}
public static void main(String[] args)
{
String word = "stackoverflow";
String[] book = new String[] {
"st", "ck", "CAG", "low", "TC",
"rf", "ove", "a", "sta"
};
int result = optimal(word, book);
final int cycles = 50;
long averageTime = benchmarkOptima(cycles, word, book);
System.out.println("Optimal result: " + result);
System.out.println("Average execution time - " + averageTime + " ms");
}
Output
2
Average execution time - 6 ms
Note: The implementation is getting stuck in the test case mentioned by #user1221, working on it.
What I could think of is a Trie based approach that is O(sum of length of words in dict) space. Time is not optimal.
Procedure:
Build a Trie of all the words in the dictionary. This is a pre-processing task that will take O(sum of lengths of all strings in dict).
We try finding the string that you want to make in the trie, with a twist. We start with searching a prefix of the string. If we get a prefix in the trie, we start the search from the top recursively and continue to look for more prefixes.
When we reach the end of out string i.e. stackoverflow, we check if we arrived at the end of any string, if yes, then we reached a valid combination of this string. we count this while going back up the recursion.
eg:
In the above case, we use the dict as {"st", "sta", "a", "ck"}
We construct our trie ($ is the sentinel char, i.e. a char which is not in the dict):
$___s___t.___a.
|___a.
|___c___k.
the . represents that a word in the dict ends at that position.
We try to find the no of constructions of stack.
We start searching stack in the trie.
depth=0
$___s(*)___t.___a.
|___a.
|___c___k.
We see that we are at the end of one word, we start a new search with the remaining string ack from the top.
depth=0
$___s___t(*).___a.
|___a.
|___c___k.
Again we are at the end of one word in the dict. We start a new search for ck.
depth=1
$___s___t.___a.
|___a(*).
|___c___k.
depth=2
$___s___t.___a.
|___a.
|___c(*)___k.
We reach the end of stack and end of a word in the dict, hence we have 1 valid representation of stack.
depth=2
$___s___t.___a.
|___a.
|___c___k(*).
We go back to the caller of depth=2
No next char is available, we return to the caller of depth=1.
depth=1
$___s___t.___a.
|___a(*, 1).
|___c___k.
depth=0
$___s___t(*, 1).___a.
|___a.
|___c___k.
We move to next char. We see that we reached the end of one word in the dict, we launch a new search for ck in the dict.
depth=0
$___s___t.___a(*, 1).
|___a.
|___c___k.
depth=1
$___s___t.___a.
|___a.
|___c(*)___k.
We reach the end of the stack and a work in the dict, so another valid representation. We go back to the caller of depth=1
depth=1
$___s___t.___a.
|___a.
|___c___k(*, 1).
There are no more chars to proceed, we return with the result 2.
depth=0
$___s___t.___a(*, 2).
|___a.
|___c___k.
Note: The implementation is in C++, shouldn't be too hard to convert to Java and this implementation assumes that all chars are lowercase, it's trivial to extend it to both cases.
Sample code (full version):
/**
Node *base: head of the trie
Node *h : current node in the trie
string s : string to search
int idx : the current position in the string
*/
int count(Node *base, Node *h, string s, int idx) {
// step 3: found a valid combination.
if (idx == s.size()) return h->end;
int res = 0;
// step 2: we recursively start a new search.
if (h->end) {
res += count(base, base, s, idx);
}
// move ahead in the trie.
if (h->next[s[idx] - 'a'] != NULL) {
res += count(base, h->next[s[idx] - 'a'], s, idx + 1);
}
return res;
}
def cancons(target,wordbank, memo={}):
if target in memo:
return memo[target]
if target =='':
return 1
total_count =0
for word in wordbank:
if target.startswith(word):
l= len(word)
number_of_way=cancons(target[l:],wordbank,memo)
total_count += number_of_way
memo[target]= total_count
return total_count
if __name__ == '__main__':
word = "stackoverflow";
String= ["st", "ck","CAG","low","TC","rf","ove","a","sta"]
b=cancons(word,String,memo={})
print(b)
I recently took a quiz asking me to determine if elements in an array were anagrams. I completed an implementation, but upon running their tests, I only passed 1 of 5 test cases. The problem is, they wouldn't allow me to see what the tests were, so I'm really unsure about what I failed on. I've recreated my answer below, which basically multiplies the letters in a word and adds this number to an array. It then compares the numbers in one array to the numbers in the other, and prints true if they are the same. I'm basically asking what are some scenarios in which this would fail, and how would I modify this code to account for these cases?
public class anagramFinder {
public static void main (String[] args){
String[] listOne = new String[5];
listOne[0] = "hello";
listOne[1] = "lemon";
listOne[2] = "cheese";
listOne[3] = "man";
listOne[4] = "touch";
String[] listTwo = new String[5];
listTwo[0] = "olleh";
listTwo[1] = "melon";
listTwo[2] = "house";
listTwo[3] = "namer";
listTwo[4] = "tou";
isAnagram(listOne,listTwo);
}
public static void isAnagram(String[] firstWords, String[] secondWords){
int firstTotal = 1;
int secondTotal = 1;
int[] theFirstInts = new int[firstWords.length];
int[] theSecondInts = new int[secondWords.length];
for(int i = 0;i<firstWords.length;i++){
for(int j = 0;j<firstWords[i].length();j++){
firstTotal = firstTotal * firstWords[i].charAt(j);
}
theFirstInts[i] = firstTotal;
firstTotal = 1;
}
for(int i = 0;i<secondWords.length;i++){
for(int j = 0;j<secondWords[i].length();j++){
secondTotal = secondTotal * secondWords[i].charAt(j);
}
theSecondInts[i] = secondTotal;
secondTotal = 1;
}
for(int i=0;i<minimum(theFirstInts.length,theSecondInts.length);i++){
if(theFirstInts[i] == theSecondInts[i]){
System.out.println("True");
} else {
System.out.println("False");
}
}
}
public static int minimum(int number,int otherNumber){
if(number<otherNumber){
return number;
} else {
return otherNumber;
}
}
}
In my above example that I run in the main method, this prints True True False False False, which is correct
Copying my answer from a similar question.
Here's a simple fast O(n) solution without using sorting or multiple loops or hash maps. We increment the count of each character in the first array and decrement the count of each character in the second array. If the resulting counts array is full of zeros, the strings are anagrams. Can be expanded to include other characters by increasing the size of the counts array.
class AnagramsFaster{
private static boolean compare(String a, String b){
char[] aArr = a.toLowerCase().toCharArray(), bArr = b.toLowerCase().toCharArray();
if (aArr.length != bArr.length)
return false;
int[] counts = new int[26]; // An array to hold the number of occurrences of each character
for (int i = 0; i < aArr.length; i++){
counts[aArr[i]-97]++; // Increment the count of the character at i
counts[bArr[i]-97]--; // Decrement the count of the character at i
}
// If the strings are anagrams, the counts array will be full of zeros
for (int i = 0; i<26; i++)
if (counts[i] != 0)
return false;
return true;
}
public static void main(String[] args){
System.out.println(compare(args[0], args[1]));
}
}
The idea of multiplying ASCII codes isn't bad, but not perfect. It would need a deep analysis to show that two different words could have the same products, with the given range of 'a' to 'z', and within reasonable length.
One conventional approach would be to create a Map for counting the letters, and compare the Maps.
Another one would sort the letters and compare the sorted strings.
A third one would iterate over the letters of the first word, try to locate the letter in the second word, and reduce the second word by that letter.
I can't think of a fourth way, but I'm almost certain there is one ;-)
Later
Well, here's a fourth way: assign 26 prime numbers to 'a' to 'z' and (using BigInteger) multiply the primes according to the letters of a word. Anagrams produce identical products.
I was trying out this question :
Write a function using Recursion to display all anagrams of a string entered by the user, in such a way that all its vowels are located at the end of every anagram. (E.g.: Recursion => Rcrsneuio, cRsnroieu, etc.) Optimize it.
From this site :
http://erwnerve.tripod.com/prog/recursion/magic.htm
This is what i have done :
public static void permute(char[] pre,char[] suff) {
if (isEmpty(suff)) {
//result is a set of string. toString() method will return String representation of the array.
result.add(toString(moveVowelstoEnd(pre)));
return;
}
int sufflen = getLength(suff); //gets the length of the array
for(int i =0;i<sufflen;i++) {
char[] tempPre = pre.clone();
char[] tempSuf = suff.clone();
int nextindex = getNextIndex(pre); //find the next empty spot in the prefix array
tempPre[nextindex] = tempSuf[i];
tempSuf = removeElement(i,tempSuf); //removes the element at i and shifts array to the left
permute(tempPre,tempSuf);
}
}
public static char[] moveVowelstoEnd(char[] input) {
int c = 0;
for(int i =0;i<input.length;i++) {
if(c>=input.length)
break;
char ch = input[i];
if (vowels.contains(ch+"")) {
c++;
int j = i;
for(;j<input.length-1;j++)
input[j] = input[j+1];
input[j]=ch;
i--;
}
}
return input;
}
Last part of the question is 'Optimize it'. I am not sure how to optimize this. can any one help?
Group all the vowels into v
Group all consonants into w
For every pair of anagrams, concat the results
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);
}
}