Construct the largest possible rectangle out of line segments of given lengths - algorithm
I recently participated in a competition where I was asked this question. Given an array with lengths what is the area of the biggest rectangle that can be made using ALL the lengths. The lengths can be added but not broken in between.
Example:
[ 4,2,4,4,6,8 ] given this array the best we can do is make a rectangle of sides 8 and 6 like this.
giving an area of 8 * 6 = 48.
I am a beginner and even after a long hard think about how to do it I am unable to get anywhere. I am not looking for a solution but any clue to nudge me in the right direction would be appreciated.
TIA
Edit: Somebody pointed out(comment deleted now) that its difficult to explain the solution with just hints and not posting some code. Kindly post code if necessary.
The problem is NP-Hard, thus the backtracking solution [or other exponential solution as suggested by #vhallac] will be your best shot, since there is not known [and if P!=NP, there is no existing] polynomial solution for this kind of problem.
NP-Hardness proof:
First, we know that a rectangle consists of 4 edges, that are equal in pairs [e1=e2,e3=e4].
We will show that if there is a polynomial algorithm A to this problem, we can also solve the Partition Problem, by the following algorithm:
input: a group of numbers S=a1,a2,...,an
output: true if and only if the numbers can be partitioned
algorithm:
sum <- a1 + a2 + .. + an
lengths <- a1, a2 , ... , an , (sum*5), (sum*5)
activate A with lengths.
if A answered there is any rectangle [solution is not 0], answer True
else answer False
Correctness:
(1) if there is a partition to S, let it be S1,S2, there is also a rectangle with edges: (sum*5),(sum*5),S1,S2, and the algorithm will yield True.
(2) if the algorithm yields True, there is a rectangle available in lengths, since a1 + a2 + ... + an < sum*5, there are 2 edges with length sum*5, since the 2 other edges must be made using all remaining lengths [as the question specified], each other edge is actually of length (a1 + a2 + ... + an)/2, and thus there is a legal partition to the problem.
Conclusion: There is a reduction PARTITION<=(p) this problem, and thus, this problem is NP-Hard
EDIT:
the backtracking solution is pretty simple, get all possible rectangles, and check each of them to see which is the best.
backtracking solution: pseudo-code:
getAllRectangles(S,e1,e2,e3,e4,sol):
if S == {}:
if legalRectangle(e1,e2,e3,e4):
sol.add((e1,e2,e3,e4))
else: //S is not empty
elem <- S[0]
getAllRectangles(S-elem,e1+elem,e2,e3,e4,sol)
getAllRectangles(S-elem,e1,e2+elem,e3,e4,sol)
getAllRectangles(S-elem,e1,e2,e3+elem,e4,sol)
getAllRectangles(S-elem,e1,e2,e3,e4+elem,sol)
getRectangle(S):
RECS <- new Set
getAllRectangles(S,{},{},{},{},RECS)
getBest(RECS)
EDIT2:
As discussed in the comments, this answer shows not only this is hard to find the BEST rectangle, it is also hard to find ANY rectangle, making this problem hard for heuristic solutions as well.
Here is one solution to the problem in Python. It is not optimized at all. I even check 2, 4 after I check 4,2, for example. But for showing how you can find a solution, I think it is good enough.
def all_but(lst, pos):
return lst[0:pos]+lst[pos+1:]
def find_sets_with_len(segs, l):
for i in range(0, len(segs)):
val = segs[i]
if (val == l):
yield [val], all_but(segs, i)
if (val < l):
for soln, rest in find_sets_with_len(all_but(segs, i), l - val):
yield [val]+soln, rest
def find_rect(segs, l1, l2):
for side1, rest1 in find_sets_with_len(segs, l1):
for side2, rest2 in find_sets_with_len(rest1, l1):
for side3, rest3 in find_sets_with_len(rest2, l2):
return [side1, side2, side3, rest3]
def make_rect(segs):
tot_len = sum(segs)
if (tot_len %2) == 0:
opt_len=tot_len/4
for l in range(opt_len, 0, -1):
sides = find_rect(segs, l, tot_len/2-l)
if sides is not None:
print(sides)
return sides
print("Can't find any solution")
make_rect([4,2,4,4,6,8])
The idea is simple: first, calculate the optimal length (that is, the length to make a square), then search everything starting off with the optimal length, and go down to 1 for one side. For each length, enumerate all sets for one side of the claculated length, then enumerate all sets for the opposite side (of the same length), then if I can find one more set of the remaining length (that is total_len/2 minus the side length I am looking at), then I've got the best solution. This happens in find_rect() function.
Well, I get little bit bored so play around with Java to have some experience, can be poorly coded and without tuning, as I am trying to increase my coding skill, comments are welcome. My computer able to answer me for small arrays:)
Output looks like:
Largest rectangle range is ; 48
-------------------------------------------------
input values; [ 4,2,4,4,6,8,9 ]
-------------------------------------------------
Array details of the rectangle:
A1: [ 6 ]
B1: [ 8 ]
A2: [ 2,4 ]
B2: [ 4,4 ]
combination.class using Kenneth algorithm;
import java.math.BigInteger;
public class Combination {
/**
* Burak
*/
private int[] a;
private int n;
private int r;
private BigInteger numLeft;
private BigInteger total;
public Combination (int n, int r) {
if (r > n) {
throw new IllegalArgumentException ();
}
if (n < 1) {
throw new IllegalArgumentException ();
}
this.n = n;
this.r = r;
a = new int[r];
BigInteger nFact = getFactorial (n);
BigInteger rFact = getFactorial (r);
BigInteger nminusrFact = getFactorial (n - r);
total = nFact.divide (rFact.multiply (nminusrFact));
reset ();
}
//------
// Reset
//------
public void reset () {
for (int i = 0; i < a.length; i++) {
a[i] = i;
}
numLeft = new BigInteger (total.toString ());
}
//------------------------------------------------
// Return number of combinations not yet generated
//------------------------------------------------
public BigInteger getNumLeft () {
return numLeft;
}
//-----------------------------
// Are there more combinations?
//-----------------------------
public boolean hasMore () {
return numLeft.compareTo (BigInteger.ZERO) == 1;
}
//------------------------------------
// Return total number of combinations
//------------------------------------
public BigInteger getTotal () {
return total;
}
//------------------
// Compute factorial
//------------------
private static BigInteger getFactorial (int n) {
BigInteger fact = BigInteger.ONE;
for (int i = n; i > 1; i--) {
fact = fact.multiply (new BigInteger (Integer.toString (i)));
}
return fact;
}
//--------------------------------------------------------
// Generate next combination (algorithm from Rosen p. 286)
//--------------------------------------------------------
public int[] getNext () {
if (numLeft.equals (total)) {
numLeft = numLeft.subtract (BigInteger.ONE);
return a;
}
int i = r - 1;
while (a[i] == n - r + i) {
i--;
}
a[i] = a[i] + 1;
for (int j = i + 1; j < r; j++) {
a[j] = a[i] + j - i;
}
numLeft = numLeft.subtract (BigInteger.ONE);
return a;
}
}
And main Combinator.class;
import java.util.*;
public class Combinator {
/**
* #param args
*/
private static int[] ad;
private static int[] bd;
private static String a1;
private static String a2;
private static String b1;
private static String b2;
private static int bestTotal =0;
public static void main(String[] args) {
int[] array={4,2,4,4,6,8,9};
getBestCombination(array, 1);
if(bestTotal <= 0){
System.out.println("System couldnt create any rectangle.");
}else{
System.out.println("Largest rectangle range is ; " + bestTotal);
System.out.println("-------------------------------------------------");
System.out.println("input values; " + parseArrayToString(array));
System.out.println("-------------------------------------------------");
System.out.println("Array details of the rectangle:");
System.out.println("A1: " + a1);
System.out.println("B1: " + b1);
System.out.println("A2: " + a2);
System.out.println("B2: " + b2);
}
}
private static void getBestCombination(int[] array, int level){
int[] a;
int[] b;
int bestPerimeter = getTotal(array,true);
Vector<Vector<Integer>> results = null;
for(int o=array.length-1;o>=1;o--){
for(int u=bestPerimeter;u>=1;u--){
results = Combinator.compute (array, o, u);
if(results.size() > 0){
for(int i=0;i<results.size();i++){
a = new int[results.elementAt(i).size()];
for(int j = 0;j<results.elementAt(i).size();j++){
a[j] = results.elementAt(i).elementAt(j);
}
b = removeItems(array, results.elementAt(i));
if(level == 1){
getBestCombination(a,2);
getBestCombination(b,3);
}else if(level == 2){
ad = a;
bd = b;
}else{
getBestCombination(a,4);
getBestCombination(b,4);
if(getTotal(ad, false) == getTotal(a, false) && getTotal(bd, false) == getTotal(b, false)){
if(bestTotal<(getTotal(ad, false)*getTotal(bd, false))){
bestTotal = getTotal(ad, false)*getTotal(bd, false);
a1 = parseArrayToString(ad);
a2 = parseArrayToString(a);
b1 = parseArrayToString(bd);
b2 = parseArrayToString(b);
}
}else if(getTotal(ad, false) == getTotal(b, false) && getTotal(bd, false) == getTotal(a, false)){
if(bestTotal<(getTotal(ad, false)*getTotal(bd, false))){
bestTotal = getTotal(ad, false)*getTotal(bd, false);
a1 = parseArrayToString(ad);
a2 = parseArrayToString(b);
b1 = parseArrayToString(bd);
b2 = parseArrayToString(a);
}
}
}
}
}
}
}
}
private static String parseArrayToString(int[] items){
String s = "[ ";
for(int i=0;i<items.length;i++){
if(i!=items.length-1){
s = s + items[i] + ",";
}else{
s = s + items[i];
}
}
s = s + " ]";
return s;
}
#SuppressWarnings("rawtypes")
private static int[] removeItems(int[] array, Vector items){
ArrayList<Integer> res = new ArrayList<Integer>();
for(int i=0;i<array.length;i++){
res.add(array[i]);
}
for(int u = 0;u<items.size();u++){
res.remove(items.elementAt(u));
}
int[] results = new int[res.size()];
for(int o=0;o<res.size();o++){
results[o] = res.get(o);
}
return results;
}
private static int getTotal(int[] array,boolean bestPerimeter){
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
if(bestPerimeter == true){
if(sum%2!=0){
sum = sum -1;
}
sum = sum/2;
}
//System.out.println(sum);
return sum;
}
#SuppressWarnings("rawtypes")
private static int getSum (Vector v) {
int sum = 0;
Integer n;
for (int i = 0; i < v.size (); i++) {
n = (Integer) v.elementAt(i);
sum += n.intValue ();
}
return sum;
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public static Vector<Vector<Integer>> compute (int[] array, int atATime, int desiredTotal) {
int[] indices;
Combination gen = new Combination (array.length, atATime);
Vector results = new Vector ();
Vector combination;
int sum;
Integer intObj;
while (gen.hasMore ()) {
combination = new Vector ();
indices = gen.getNext ();
for (int i = 0; i < indices.length; i++) {
intObj = new Integer (array[indices[i]]);
combination.addElement (intObj);
}
sum = getSum (combination);
if (sum == desiredTotal) {
Collections.sort (combination);
if (!results.contains (combination)) {
results.addElement (combination);
}
}
}
return results;
}
}
Related
Print all palindromes of size greater than equal to 3 of a given string [duplicate]
If the input is 'abba' then the possible palindromes are a, b, b, a, bb, abba. I understand that determining if string is palindrome is easy. It would be like: public static boolean isPalindrome(String str) { int len = str.length(); for(int i=0; i<len/2; i++) { if(str.charAt(i)!=str.charAt(len-i-1) { return false; } return true; } But what is the efficient way of finding palindrome substrings?
This can be done in O(n), using Manacher's algorithm. The main idea is a combination of dynamic programming and (as others have said already) computing maximum length of palindrome with center in a given letter. What we really want to calculate is radius of the longest palindrome, not the length. The radius is simply length/2 or (length - 1)/2 (for odd-length palindromes). After computing palindrome radius pr at given position i we use already computed radiuses to find palindromes in range [i - pr ; i]. This lets us (because palindromes are, well, palindromes) skip further computation of radiuses for range [i ; i + pr]. While we search in range [i - pr ; i], there are four basic cases for each position i - k (where k is in 1,2,... pr): no palindrome (radius = 0) at i - k (this means radius = 0 at i + k, too) inner palindrome, which means it fits in range (this means radius at i + k is the same as at i - k) outer palindrome, which means it doesn't fit in range (this means radius at i + k is cut down to fit in range, i.e because i + k + radius > i + pr we reduce radius to pr - k) sticky palindrome, which means i + k + radius = i + pr (in that case we need to search for potentially bigger radius at i + k) Full, detailed explanation would be rather long. What about some code samples? :) I've found C++ implementation of this algorithm by Polish teacher, mgr Jerzy Wałaszek. I've translated comments to english, added some other comments and simplified it a bit to be easier to catch the main part. Take a look here. Note: in case of problems understanding why this is O(n), try to look this way: after finding radius (let's call it r) at some position, we need to iterate over r elements back, but as a result we can skip computation for r elements forward. Therefore, total number of iterated elements stays the same.
Perhaps you could iterate across potential middle character (odd length palindromes) and middle points between characters (even length palindromes) and extend each until you cannot get any further (next left and right characters don't match). That would save a lot of computation when there are no many palidromes in the string. In such case the cost would be O(n) for sparse palidrome strings. For palindrome dense inputs it would be O(n^2) as each position cannot be extended more than the length of the array / 2. Obviously this is even less towards the ends of the array. public Set<String> palindromes(final String input) { final Set<String> result = new HashSet<>(); for (int i = 0; i < input.length(); i++) { // expanding even length palindromes: expandPalindromes(result,input,i,i+1); // expanding odd length palindromes: expandPalindromes(result,input,i,i); } return result; } public void expandPalindromes(final Set<String> result, final String s, int i, int j) { while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) { result.add(s.substring(i,j+1)); i--; j++; } }
So, each distinct letter is already a palindrome - so you already have N + 1 palindromes, where N is the number of distinct letters (plus empty string). You can do that in single run - O(N). Now, for non-trivial palindromes, you can test each point of your string to be a center of potential palindrome - grow in both directions - something that Valentin Ruano suggested. This solution will take O(N^2) since each test is O(N) and number of possible "centers" is also O(N) - the center is either a letter or space between two letters, again as in Valentin's solution. Note, there is also O(N) solution to your problem, based on Manacher's algoritm (article describes "longest palindrome", but algorithm could be used to count all of them)
I just came up with my own logic which helps to solve this problem. Happy coding.. :-) System.out.println("Finding all palindromes in a given string : "); subPal("abcacbbbca"); private static void subPal(String str) { String s1 = ""; int N = str.length(), count = 0; Set<String> palindromeArray = new HashSet<String>(); System.out.println("Given string : " + str); System.out.println("******** Ignoring single character as substring palindrome"); for (int i = 2; i <= N; i++) { for (int j = 0; j <= N; j++) { int k = i + j - 1; if (k >= N) continue; s1 = str.substring(j, i + j); if (s1.equals(new StringBuilder(s1).reverse().toString())) { palindromeArray.add(s1); } } } System.out.println(palindromeArray); for (String s : palindromeArray) System.out.println(s + " - is a palindrome string."); System.out.println("The no.of substring that are palindrome : " + palindromeArray.size()); } Output:- Finding all palindromes in a given string : Given string : abcacbbbca ******** Ignoring single character as substring palindrome ******** [cac, acbbbca, cbbbc, bb, bcacb, bbb] cac - is a palindrome string. acbbbca - is a palindrome string. cbbbc - is a palindrome string. bb - is a palindrome string. bcacb - is a palindrome string. bbb - is a palindrome string. The no.of substring that are palindrome : 6
I suggest building up from a base case and expanding until you have all of the palindomes. There are two types of palindromes: even numbered and odd-numbered. I haven't figured out how to handle both in the same way so I'll break it up. 1) Add all single letters 2) With this list you have all of the starting points for your palindromes. Run each both of these for each index in the string (or 1 -> length-1 because you need at least 2 length): findAllEvenFrom(int index){ int i=0; while(true) { //check if index-i and index+i+1 is within string bounds if(str.charAt(index-i) != str.charAt(index+i+1)) return; // Here we found out that this index isn't a center for palindromes of >=i size, so we can give up outputList.add(str.substring(index-i, index+i+1)); i++; } } //Odd looks about the same, but with a change in the bounds. findAllOddFrom(int index){ int i=0; while(true) { //check if index-i and index+i+1 is within string bounds if(str.charAt(index-i-1) != str.charAt(index+i+1)) return; outputList.add(str.substring(index-i-1, index+i+1)); i++; } } I'm not sure if this helps the Big-O for your runtime, but it should be much more efficient than trying each substring. Worst case would be a string of all the same letter which may be worse than the "find every substring" plan, but with most inputs it will cut out most substrings because you can stop looking at one once you realize it's not the center of a palindrome.
I tried the following code and its working well for the cases Also it handles individual characters too Few of the cases which passed: abaaa --> [aba, aaa, b, a, aa] geek --> [g, e, ee, k] abbaca --> [b, c, a, abba, bb, aca] abaaba -->[aba, b, abaaba, a, baab, aa] abababa -->[aba, babab, b, a, ababa, abababa, bab] forgeeksskeegfor --> [f, g, e, ee, s, r, eksske, geeksskeeg, o, eeksskee, ss, k, kssk] Code static Set<String> set = new HashSet<String>(); static String DIV = "|"; public static void main(String[] args) { String str = "abababa"; String ext = getExtendedString(str); // will check for even length palindromes for(int i=2; i<ext.length()-1; i+=2) { addPalindromes(i, 1, ext); } // will check for odd length palindromes including individual characters for(int i=1; i<=ext.length()-2; i+=2) { addPalindromes(i, 0, ext); } System.out.println(set); } /* * Generates extended string, with dividors applied * eg: input = abca * output = |a|b|c|a| */ static String getExtendedString(String str) { StringBuilder builder = new StringBuilder(); builder.append(DIV); for(int i=0; i< str.length(); i++) { builder.append(str.charAt(i)); builder.append(DIV); } String ext = builder.toString(); return ext; } /* * Recursive matcher * If match is found for palindrome ie char[mid-offset] = char[mid+ offset] * Calculate further with offset+=2 * * */ static void addPalindromes(int mid, int offset, String ext) { // boundary checks if(mid - offset <0 || mid + offset > ext.length()-1) { return; } if (ext.charAt(mid-offset) == ext.charAt(mid+offset)) { set.add(ext.substring(mid-offset, mid+offset+1).replace(DIV, "")); addPalindromes(mid, offset+2, ext); } } Hope its fine
public class PolindromeMyLogic { static int polindromeCount = 0; private static HashMap<Character, List<Integer>> findCharAndOccurance( char[] charArray) { HashMap<Character, List<Integer>> map = new HashMap<Character, List<Integer>>(); for (int i = 0; i < charArray.length; i++) { char c = charArray[i]; if (map.containsKey(c)) { List list = map.get(c); list.add(i); } else { List list = new ArrayList<Integer>(); list.add(i); map.put(c, list); } } return map; } private static void countPolindromeByPositions(char[] charArray, HashMap<Character, List<Integer>> map) { map.forEach((character, list) -> { int n = list.size(); if (n > 1) { for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if (list.get(i) + 1 == list.get(j) || list.get(i) + 2 == list.get(j)) { polindromeCount++; } else { char[] temp = new char[(list.get(j) - list.get(i)) + 1]; int jj = 0; for (int ii = list.get(i); ii <= list .get(j); ii++) { temp[jj] = charArray[ii]; jj++; } if (isPolindrome(temp)) polindromeCount++; } } } } }); } private static boolean isPolindrome(char[] charArray) { int n = charArray.length; char[] temp = new char[n]; int j = 0; for (int i = (n - 1); i >= 0; i--) { temp[j] = charArray[i]; j++; } if (Arrays.equals(charArray, temp)) return true; else return false; } public static void main(String[] args) { String str = "MADAM"; char[] charArray = str.toCharArray(); countPolindromeByPositions(charArray, findCharAndOccurance(charArray)); System.out.println(polindromeCount); } } Try out this. Its my own solution.
// Maintain an Set of palindromes so that we get distinct elements at the end // Add each char to set. Also treat that char as middle point and traverse through string to check equality of left and right char static int palindrome(String str) { Set<String> distinctPln = new HashSet<String>(); for (int i=0; i<str.length();i++) { distinctPln.add(String.valueOf(str.charAt(i))); for (int j=i-1, k=i+1; j>=0 && k<str.length(); j--, k++) { // String of lenght 2 as palindrome if ( (new Character(str.charAt(i))).equals(new Character(str.charAt(j)))) { distinctPln.add(str.substring(j,i+1)); } // String of lenght 2 as palindrome if ( (new Character(str.charAt(i))).equals(new Character(str.charAt(k)))) { distinctPln.add(str.substring(i,k+1)); } if ( (new Character(str.charAt(j))).equals(new Character(str.charAt(k)))) { distinctPln.add(str.substring(j,k+1)); } else { continue; } } } Iterator<String> distinctPlnItr = distinctPln.iterator(); while ( distinctPlnItr.hasNext()) { System.out.print(distinctPlnItr.next()+ ","); } return distinctPln.size(); }
Code is to find all distinct substrings which are palindrome. Here is the code I tried. It is working fine. import java.util.HashSet; import java.util.Set; public class SubstringPalindrome { public static void main(String[] args) { String s = "abba"; checkPalindrome(s); } public static int checkPalindrome(String s) { int L = s.length(); int counter =0; long startTime = System.currentTimeMillis(); Set<String> hs = new HashSet<String>(); // add elements to the hash set System.out.println("Possible substrings: "); for (int i = 0; i < L; ++i) { for (int j = 0; j < (L - i); ++j) { String subs = s.substring(j, i + j + 1); counter++; System.out.println(subs); if(isPalindrome(subs)) hs.add(subs); } } System.out.println("Total possible substrings are "+counter); System.out.println("Total palindromic substrings are "+hs.size()); System.out.println("Possible palindromic substrings: "+hs.toString()); long endTime = System.currentTimeMillis(); System.out.println("It took " + (endTime - startTime) + " milliseconds"); return hs.size(); } public static boolean isPalindrome(String s) { if(s.length() == 0 || s.length() ==1) return true; if(s.charAt(0) == s.charAt(s.length()-1)) return isPalindrome(s.substring(1, s.length()-1)); return false; } } OUTPUT: Possible substrings: a b b a ab bb ba abb bba abba Total possible substrings are 10 Total palindromic substrings are 4 Possible palindromic substrings: [bb, a, b, abba] It took 1 milliseconds
Maximal Nested Intervals Set
This is a problem based on finding size of maximal nested intervals set.. There are many intervals each defined by pair containing a starting point and a ending point (si, ei). Two intervals i1 and i2 are called nested if i2 lies completely inside of i1. Example:- (2,6) and (3,4) are nested, since (3,4) is a part of (2,6). Similarly k intervals i1, i2, i3....ik are called nested if, i2 lies inside i1, i3 lies inside i2, ...and so on. Determine size of largest set of intervals from the given intervals, such that all the intervals in that set could produce a nesting. I thought it in following way:- Let us take an e.g.- (0,7) (0,5) (1,21) (1,9) (2,8) (2,4) (3,20) (4,16) (5,15) (6,21) Sort it in the way such that a[i-1]<=a[i] && b[i-1]>=b[i] Than starting from first interval we start a linked list.. if the next interval comes inside a interval we move down the node and traverse the graph created down the node(other than main list)..we store the pointer of the maximum level node in this graph at which the new interval can fit.. and traverse further in linked list to see under whom the current interval comes.. at last we have a pointer to a node with whom we have to attach the current interval.. and compare the level of this node with the maximum level we already have..... The final value of maximum level is size of maximal nested intervals set.. complexity of above solution is likely to be:- O(n(k+l) + nlogn) I guess it is difficult to get it this way, but I have no other option... if anyone got any other algorithm to solve it.. please post because my algorithm will take much longer to implement(lot of data structures involved)... thanks!!!
Edit: A few solutions to the problem are posted here, including two that claim to be O(n lg n). However, I don't think the O(n lg n) solutions work. I made comments on that page indicating why. If anyone has an O(n lg n) solution, I'd love to hear it. Quadratic Solution This problem can be solved in O(n^2) time using dynamic programming: Compute how many intervals each interval contains (can be done with two nested loops) Sort the intervals in ascending order of number of contained intervals Use the recurrence MaxNestedIntervals to solve the problem *Note: Step 1 can be done in O(n lg n) time using the solution here: Sub O(n^2) algorithm for counting nested intervals? Step 2 can be done in O(n lg n) time with any optimal comparison-based sorting algorithm. There may be a way to optimize step 3, but I haven't found it yet. Recurrence MaxNestedIntervals(i) = max {j = 0 to i-1} : 1 + MaxNestedIntervals(j) if interval i contains interval j 0 if interval i doesn't contain interval j Base case MaxNestedIntervals(i) = 0 if interval i contains 0 intervals 1 if interval i contains 1 interval Sample code import java.util.*; public class MaxNestedIntervals { public static void main(String[] args) { Interval[] intervals = new Interval[10]; intervals[0] = new Interval(0, 7); intervals[1] = new Interval(0, 5); intervals[2] = new Interval(1, 21); intervals[3] = new Interval(1, 9); intervals[4] = new Interval(2, 8); intervals[5] = new Interval(2, 4); intervals[6] = new Interval(3, 20); intervals[7] = new Interval(4,16); intervals[8] = new Interval(5,15); intervals[9] = new Interval(6,21); int n = intervals.length; AugmentedInterval[] augmentedIntervals = new AugmentedInterval[n]; for (int i = 0; i < n; i++) { augmentedIntervals[i] = new AugmentedInterval(intervals[i]); } for (int i = 0; i < n; i++) { AugmentedInterval outerInterval = augmentedIntervals[i]; for (int j = 0; j < n; j++) { if (i == j) { continue; } AugmentedInterval innerInterval = augmentedIntervals[j]; if (outerInterval.contains(innerInterval)) { outerInterval.numContainedIntervals++; if (outerInterval.childInterval == null) { outerInterval.childInterval = innerInterval; } } } } Arrays.sort(augmentedIntervals, new Comparator<AugmentedInterval>() { public int compare(AugmentedInterval i, AugmentedInterval j) { return i.numContainedIntervals - j.numContainedIntervals; } }); int maxNestedIntervals = 0; AugmentedInterval parentInterval = null; for (int i = 0; i < n; i++) { AugmentedInterval currentInterval = augmentedIntervals[i]; if (currentInterval.numContainedIntervals == 0) { currentInterval.maxNestedIntervals = 0; } else if (currentInterval.numContainedIntervals == 1) { currentInterval.maxNestedIntervals = 1; } else { int maxNestedIntervalsForCurrentInterval = 0; for (int j = 0; j < i; j++) { AugmentedInterval candidateNestedInterval = augmentedIntervals[j]; int maxNestedIntervalsForCurrentCandidate = candidateNestedInterval.maxNestedIntervals + 1; if (currentInterval.contains(candidateNestedInterval) && maxNestedIntervalsForCurrentCandidate >= maxNestedIntervalsForCurrentInterval) { maxNestedIntervalsForCurrentInterval = maxNestedIntervalsForCurrentCandidate; currentInterval.childInterval = candidateNestedInterval; } } currentInterval.maxNestedIntervals = maxNestedIntervalsForCurrentInterval; if (maxNestedIntervalsForCurrentInterval >= maxNestedIntervals) { maxNestedIntervals = maxNestedIntervalsForCurrentInterval; parentInterval = currentInterval; } } } if (n == 0) { System.out.println("The largest set of nested intervals is the empty set."); } else if (maxNestedIntervals == 0) { System.out.println("The largest set of nested intervals has 1 interval.\n"); System.out.println("That interval is:"); } else { System.out.println("The largest set of nested intervals has " + (maxNestedIntervals + 1) + " intervals.\n"); System.out.println("Those intervals are:"); } for (AugmentedInterval currentInterval = parentInterval; currentInterval != null; currentInterval = currentInterval.childInterval) { System.out.println(currentInterval); } System.out.println(); } private static class Interval implements Comparable<Interval> { public int start = 0; public int end = 0; public Interval(int start, int end) { this.start = start; this.end = end; } public int size() { return this.end - this.start; } public boolean contains(Interval other) { return (this.start <= other.start && this.end >= other.end); } public int compareTo(Interval other) { return this.size() - other.size(); } public String toString() { return "[" + this.start + ", " + this.end + "]"; } } private static class AugmentedInterval extends Interval { public int numContainedIntervals = 0; public int maxNestedIntervals = 0; public AugmentedInterval childInterval = null; public AugmentedInterval(Interval interval) { super(interval.start, interval.end); } } }
Find a pair of elements from an array whose sum equals a given number
Given array of n integers and given a number X, find all the unique pairs of elements (a,b), whose summation is equal to X. The following is my solution, it is O(nLog(n)+n), but I am not sure whether or not it is optimal. int main(void) { int arr [10] = {1,2,3,4,5,6,7,8,9,0}; findpair(arr, 10, 7); } void findpair(int arr[], int len, int sum) { std::sort(arr, arr+len); int i = 0; int j = len -1; while( i < j){ while((arr[i] + arr[j]) <= sum && i < j) { if((arr[i] + arr[j]) == sum) cout << "(" << arr[i] << "," << arr[j] << ")" << endl; i++; } j--; while((arr[i] + arr[j]) >= sum && i < j) { if((arr[i] + arr[j]) == sum) cout << "(" << arr[i] << "," << arr[j] << ")" << endl; j--; } } }
There are 3 approaches to this solution: Let the sum be T and n be the size of array Approach 1: The naive way to do this would be to check all combinations (n choose 2). This exhaustive search is O(n2). Approach 2: A better way would be to sort the array. This takes O(n log n) Then for each x in array A, use binary search to look for T-x. This will take O(nlogn). So, overall search is O(n log n) Approach 3 : The best way would be to insert every element into a hash table (without sorting). This takes O(n) as constant time insertion. Then for every x, we can just look up its complement, T-x, which is O(1). Overall the run time of this approach is O(n). You can refer more here.Thanks.
# Let arr be the given array. # And K be the give sum for i=0 to arr.length - 1 do # key is the element and value is its index. hash(arr[i]) = i end-for for i=0 to arr.length - 1 do # if K-th element exists and it's different then we found a pair if hash(K - arr[i]) != i print "pair i , hash(K - arr[i]) has sum K" end-if end-for
Implementation in Java : Using codaddict's algorithm (Maybe slightly different) import java.util.HashMap; public class ArrayPairSum { public static void main(String[] args) { int []a = {2,45,7,3,5,1,8,9}; printSumPairs(a,10); } public static void printSumPairs(int []input, int k){ Map<Integer, Integer> pairs = new HashMap<Integer, Integer>(); for(int i=0;i<input.length;i++){ if(pairs.containsKey(input[i])) System.out.println(input[i] +", "+ pairs.get(input[i])); else pairs.put(k-input[i], input[i]); } } } For input = {2,45,7,3,5,1,8,9} and if Sum is 10 Output pairs: 3,7 8,2 9,1 Some notes about the solution : We iterate only once through the array --> O(n) time Insertion and lookup time in Hash is O(1). Overall time is O(n), although it uses extra space in terms of hash.
Solution in java. You can add all the String elements to an ArrayList of strings and return the list. Here I am just printing it out. void numberPairsForSum(int[] array, int sum) { HashSet<Integer> set = new HashSet<Integer>(); for (int num : array) { if (set.contains(sum - num)) { String s = num + ", " + (sum - num) + " add up to " + sum; System.out.println(s); } set.add(num); } }
Python Implementation: import itertools list = [1, 1, 2, 3, 4, 5,] uniquelist = set(list) targetsum = 5 for n in itertools.combinations(uniquelist, 2): if n[0] + n[1] == targetsum: print str(n[0]) + " + " + str(n[1]) Output: 1 + 4 2 + 3
C++11, run time complexity O(n): #include <vector> #include <unordered_map> #include <utility> std::vector<std::pair<int, int>> FindPairsForSum( const std::vector<int>& data, const int& sum) { std::unordered_map<int, size_t> umap; std::vector<std::pair<int, int>> result; for (size_t i = 0; i < data.size(); ++i) { if (0 < umap.count(sum - data[i])) { size_t j = umap[sum - data[i]]; result.push_back({data[i], data[j]}); } else { umap[data[i]] = i; } } return result; }
Here is a solution witch takes into account duplicate entries. It is written in javascript and assumes array is sorted. The solution runs in O(n) time and does not use any extra memory aside from variable. var count_pairs = function(_arr,x) { if(!x) x = 0; var pairs = 0; var i = 0; var k = _arr.length-1; if((k+1)<2) return pairs; var halfX = x/2; while(i<k) { var curK = _arr[k]; var curI = _arr[i]; var pairsThisLoop = 0; if(curK+curI==x) { // if midpoint and equal find combinations if(curK==curI) { var comb = 1; while(--k>=i) pairs+=(comb++); break; } // count pair and k duplicates pairsThisLoop++; while(_arr[--k]==curK) pairsThisLoop++; // add k side pairs to running total for every i side pair found pairs+=pairsThisLoop; while(_arr[++i]==curI) pairs+=pairsThisLoop; } else { // if we are at a mid point if(curK==curI) break; var distK = Math.abs(halfX-curK); var distI = Math.abs(halfX-curI); if(distI > distK) while(_arr[++i]==curI); else while(_arr[--k]==curK); } } return pairs; } I solved this during an interview for a large corporation. They took it but not me. So here it is for everyone. Start at both side of the array and slowly work your way inwards making sure to count duplicates if they exist. It only counts pairs but can be reworked to find the pairs find pairs < x find pairs > x Enjoy!
O(n) def find_pairs(L,sum): s = set(L) edgeCase = sum/2 if L.count(edgeCase) ==2: print edgeCase, edgeCase s.remove(edgeCase) for i in s: diff = sum-i if diff in s: print i, diff L = [2,45,7,3,5,1,8,9] sum = 10 find_pairs(L,sum) Methodology: a + b = c, so instead of looking for (a,b) we look for a = c - b
Implementation in Java : Using codaddict's algorithm: import java.util.Hashtable; public class Range { public static void main(String[] args) { // TODO Auto-generated method stub Hashtable mapping = new Hashtable(); int a[]= {80,79,82,81,84,83,85}; int k = 160; for (int i=0; i < a.length; i++){ mapping.put(a[i], i); } for (int i=0; i < a.length; i++){ if (mapping.containsKey(k - a[i]) && (Integer)mapping.get(k-a[i]) != i){ System.out.println(k-a[i]+", "+ a[i]); } } } } Output: 81, 79 79, 81 If you want duplicate pairs (eg: 80,80) also then just remove && (Integer)mapping.get(k-a[i]) != i from the if condition and you are good to go.
Just attended this question on HackerRank and here's my 'Objective C' Solution: -(NSNumber*)sum:(NSArray*) a andK:(NSNumber*)k { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; long long count = 0; for(long i=0;i<a.count;i++){ if(dict[a[i]]) { count++; NSLog(#"a[i]: %#, dict[array[i]]: %#", a[i], dict[a[i]]); } else{ NSNumber *calcNum = #(k.longLongValue-((NSNumber*)a[i]).longLongValue); dict[calcNum] = a[i]; } } return #(count); } Hope it helps someone.
this is the implementation of O(n*lg n) using binary search implementation inside a loop. #include <iostream> using namespace std; bool *inMemory; int pairSum(int arr[], int n, int k) { int count = 0; if(n==0) return count; for (int i = 0; i < n; ++i) { int start = 0; int end = n-1; while(start <= end) { int mid = start + (end-start)/2; if(i == mid) break; else if((arr[i] + arr[mid]) == k && !inMemory[i] && !inMemory[mid]) { count++; inMemory[i] = true; inMemory[mid] = true; } else if(arr[i] + arr[mid] >= k) { end = mid-1; } else start = mid+1; } } return count; } int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; inMemory = new bool[10]; for (int i = 0; i < 10; ++i) { inMemory[i] = false; } cout << pairSum(arr, 10, 11) << endl; return 0; }
In python arr = [1, 2, 4, 6, 10] diff_hash = {} expected_sum = 3 for i in arr: if diff_hash.has_key(i): print i, diff_hash[i] key = expected_sum - i diff_hash[key] = i
Nice solution from Codeaddict. I took the liberty of implementing a version of it in Ruby: def find_sum(arr,sum) result ={} h = Hash[arr.map {|i| [i,i]}] arr.each { |l| result[l] = sum-l if h[sum-l] && !result[sum-l] } result end To allow duplicate pairs (1,5), (5,1) we just have to remove the && !result[sum-l] instruction
Here is Java code for three approaches: 1. Using Map O(n), HashSet can also be used here. 2. Sort array and then use BinarySearch to look for complement O(nLog(n)) 3. Traditional BruteForce two loops O(n^2) public class PairsEqualToSum { public static void main(String[] args) { int a[] = {1,10,5,8,2,12,6,4}; findPairs1(a,10); findPairs2(a,10); findPairs3(a,10); } //Method1 - O(N) use a Map to insert values as keys & check for number's complement in map static void findPairs1(int[]a, int sum){ Map<Integer, Integer> pairs = new HashMap<Integer, Integer>(); for(int i=0; i<a.length; i++){ if(pairs.containsKey(sum-a[i])) System.out.println("("+a[i]+","+(sum-a[i])+")"); else pairs.put(a[i], 0); } } //Method2 - O(nlog(n)) using Sort static void findPairs2(int[]a, int sum){ Arrays.sort(a); for(int i=0; i<a.length/2; i++){ int complement = sum - a[i]; int foundAtIndex = Arrays.binarySearch(a,complement); if(foundAtIndex >0 && foundAtIndex != i) //to avoid situation where binarySearch would find the original and not the complement like "5" System.out.println("("+a[i]+","+(sum-a[i])+")"); } } //Method 3 - Brute Force O(n^2) static void findPairs3(int[]a, int sum){ for(int i=0; i<a.length; i++){ for(int j=i; j<a.length;j++){ if(a[i]+a[j] == sum) System.out.println("("+a[i]+","+a[j]+")"); } } } }
A Simple program in java for arrays having unique elements: import java.util.*; public class ArrayPairSum { public static void main(String[] args) { int []a = {2,4,7,3,5,1,8,9,5}; sumPairs(a,10); } public static void sumPairs(int []input, int k){ Set<Integer> set = new HashSet<Integer>(); for(int i=0;i<input.length;i++){ if(set.contains(input[i])) System.out.println(input[i] +", "+(k-input[i])); else set.add(k-input[i]); } } }
A simple Java code snippet for printing the pairs below: public static void count_all_pairs_with_given_sum(int arr[], int S){ if(arr.length < 2){ return; } HashSet values = new HashSet(arr.length); for(int value : arr)values.add(value); for(int value : arr){ int difference = S - value; if(values.contains(difference) && value<difference){ System.out.printf("(%d, %d) %n", value, difference); } } }
Another solution in Swift: the idea is to create an hash that store values of (sum - currentValue) and compare this to the current value of the loop. The complexity is O(n). func findPair(list: [Int], _ sum: Int) -> [(Int, Int)]? { var hash = Set<Int>() //save list of value of sum - item. var dictCount = [Int: Int]() //to avoid the case A*2 = sum where we have only one A in the array var foundKeys = Set<Int>() //to avoid duplicated pair in the result. var result = [(Int, Int)]() //this is for the result. for item in list { //keep track of count of each element to avoid problem: [2, 3, 5], 10 -> result = (5,5) if (!dictCount.keys.contains(item)) { dictCount[item] = 1 } else { dictCount[item] = dictCount[item]! + 1 } //if my hash does not contain the (sum - item) value -> insert to hash. if !hash.contains(sum-item) { hash.insert(sum-item) } //check if current item is the same as another hash value or not, if yes, return the tuple. if hash.contains(item) && (dictCount[item] > 1 || sum != item*2) // check if we have item*2 = sum or not. { if !foundKeys.contains(item) && !foundKeys.contains(sum-item) { foundKeys.insert(item) //add to found items in order to not to add duplicated pair. result.append((item, sum-item)) } } } return result } //test: let a = findPair([2,3,5,4,1,7,6,8,9,5,3,3,3,3,3,3,3,3,3], 14) //will return (8,6) and (9,5)
My Solution - Java - Without duplicates public static void printAllPairSum(int[] a, int x){ System.out.printf("printAllPairSum(%s,%d)\n", Arrays.toString(a),x); if(a==null||a.length==0){ return; } int length = a.length; Map<Integer,Integer> reverseMapOfArray = new HashMap<>(length,1.0f); for (int i = 0; i < length; i++) { reverseMapOfArray.put(a[i], i); } for (int i = 0; i < length; i++) { Integer j = reverseMapOfArray.get(x - a[i]); if(j!=null && i<j){ System.out.printf("a[%d] + a[%d] = %d + %d = %d\n",i,j,a[i],a[j],x); } } System.out.println("------------------------------"); }
This prints the pairs and avoids duplicates using bitwise manipulation. public static void findSumHashMap(int[] arr, int key) { Map<Integer, Integer> valMap = new HashMap<Integer, Integer>(); for(int i=0;i<arr.length;i++) valMap.put(arr[i], i); int indicesVisited = 0; for(int i=0;i<arr.length;i++) { if(valMap.containsKey(key - arr[i]) && valMap.get(key - arr[i]) != i) { if(!((indicesVisited & ((1<<i) | (1<<valMap.get(key - arr[i])))) > 0)) { int diff = key-arr[i]; System.out.println(arr[i] + " " +diff); indicesVisited = indicesVisited | (1<<i) | (1<<valMap.get(key - arr[i])); } } } }
I bypassed the bit manuplation and just compared the index values. This is less than the loop iteration value (i in this case). This will not print the duplicate pairs and duplicate array elements also. public static void findSumHashMap(int[] arr, int key) { Map<Integer, Integer> valMap = new HashMap<Integer, Integer>(); for (int i = 0; i < arr.length; i++) { valMap.put(arr[i], i); } for (int i = 0; i < arr.length; i++) { if (valMap.containsKey(key - arr[i]) && valMap.get(key - arr[i]) != i) { if (valMap.get(key - arr[i]) < i) { int diff = key - arr[i]; System.out.println(arr[i] + " " + diff); } } } }
in C#: int[] array = new int[] { 1, 5, 7, 2, 9, 8, 4, 3, 6 }; // given array int sum = 10; // given sum for (int i = 0; i <= array.Count() - 1; i++) if (array.Contains(sum - array[i])) Console.WriteLine("{0}, {1}", array[i], sum - array[i]);
One Solution can be this, but not optimul (The complexity of this code is O(n^2)): public class FindPairsEqualToSum { private static int inputSum = 0; public static List<String> findPairsForSum(int[] inputArray, int sum) { List<String> list = new ArrayList<String>(); List<Integer> inputList = new ArrayList<Integer>(); for (int i : inputArray) { inputList.add(i); } for (int i : inputArray) { int tempInt = sum - i; if (inputList.contains(tempInt)) { String pair = String.valueOf(i + ", " + tempInt); list.add(pair); } } return list; } }
A simple python version of the code that find a pair sum of zero and can be modify to find k: def sumToK(lst): k = 0 # <- define the k here d = {} # build a dictionary # build the hashmap key = val of lst, value = i for index, val in enumerate(lst): d[val] = index # find the key; if a key is in the dict, and not the same index as the current key for i, val in enumerate(lst): if (k-val) in d and d[k-val] != i: return True return False The run time complexity of the function is O(n) and Space: O(n) as well.
public static int[] f (final int[] nums, int target) { int[] r = new int[2]; r[0] = -1; r[1] = -1; int[] vIndex = new int[0Xfff]; for (int i = 0; i < nums.length; i++) { int delta = 0Xff; int gapIndex = target - nums[i] + delta; if (vIndex[gapIndex] != 0) { r[0] = vIndex[gapIndex]; r[1] = i + 1; return r; } else { vIndex[nums[i] + delta] = i + 1; } } return r; }
less than o(n) solution will be=> function(array,k) var map = {}; for element in array map(element) = true; if(map(k-element)) return {k,element}
Solution in Python using list comprehension f= [[i,j] for i in list for j in list if j+i==X]; O(N2) also gives two ordered pairs- (a,b) and (b,a) as well
I can do it in O(n). Let me know when you want the answer. Note it involves simply traversing the array once with no sorting, etc... I should mention too that it exploits commutativity of addition and doesn't use hashes but wastes memory. using System; using System.Collections.Generic; /* An O(n) approach exists by using a lookup table. The approach is to store the value in a "bin" that can easily be looked up(e.g., O(1)) if it is a candidate for an appropriate sum. e.g., for each a[k] in the array we simply put the it in another array at the location x - a[k]. Suppose we have [0, 1, 5, 3, 6, 9, 8, 7] and x = 9 We create a new array, indexes value 9 - 0 = 9 0 9 - 1 = 8 1 9 - 5 = 4 5 9 - 3 = 6 3 9 - 6 = 3 6 9 - 9 = 0 9 9 - 8 = 1 8 9 - 7 = 2 7 THEN the only values that matter are the ones who have an index into the new table. So, say when we reach 9 or equal we see if our new array has the index 9 - 9 = 0. Since it does we know that all the values it contains will add to 9. (note in this cause it's obvious there is only 1 possible one but it might have multiple index values in it which we need to store). So effectively what we end up doing is only having to move through the array once. Because addition is commutative we will end up with all the possible results. For example, when we get to 6 we get the index into our new table as 9 - 6 = 3. Since the table contains that index value we know the values. This is essentially trading off speed for memory. */ namespace sum { class Program { static void Main(string[] args) { int num = 25; int X = 10; var arr = new List<int>(); for(int i = 0; i <= num; i++) arr.Add((new Random((int)(DateTime.Now.Ticks + i*num))).Next(0, num*2)); Console.Write("["); for (int i = 0; i < num - 1; i++) Console.Write(arr[i] + ", "); Console.WriteLine(arr[arr.Count-1] + "] - " + X); var arrbrute = new List<Tuple<int,int>>(); var arrfast = new List<Tuple<int,int>>(); for(int i = 0; i < num; i++) for(int j = i+1; j < num; j++) if (arr[i] + arr[j] == X) arrbrute.Add(new Tuple<int, int>(arr[i], arr[j])); int M = 500; var lookup = new List<List<int>>(); for(int i = 0; i < 1000; i++) lookup.Add(new List<int>()); for(int i = 0; i < num; i++) { // Check and see if we have any "matches" if (lookup[M + X - arr[i]].Count != 0) { foreach(var j in lookup[M + X - arr[i]]) arrfast.Add(new Tuple<int, int>(arr[i], arr[j])); } lookup[M + arr[i]].Add(i); } for(int i = 0; i < arrbrute.Count; i++) Console.WriteLine(arrbrute[i].Item1 + " + " + arrbrute[i].Item2 + " = " + X); Console.WriteLine("---------"); for(int i = 0; i < arrfast.Count; i++) Console.WriteLine(arrfast[i].Item1 + " + " + arrfast[i].Item2 + " = " + X); Console.ReadKey(); } } }
I implemented logic in Scala with out a Map. It gives duplicate pairs since the counter loops thru entire elements of the array. If duplicate pairs are needed, you can simply return the value pc val arr = Array[Int](8, 7, 2, 5, 3, 1, 5) val num = 10 var pc = 0 for(i <- arr.indices) { if(arr.contains(Math.abs(arr(i) - num))) pc += 1 } println(s"Pairs: ${pc/2}") It is working with duplicates values in the array as well.
GOLANG Implementation func findPairs(slice1 []int, sum int) [][]int { pairMap := make(map[int]int) var SliceOfPairs [][]int for i, v := range slice1 { if valuei, ok := pairMap[v]; ok { //fmt.Println("Pair Found", i, valuei) SliceOfPairs = append(SliceOfPairs, []int{i, valuei}) } else { pairMap[sum-v] = i } } return SliceOfPairs }
function findPairOfNumbers(arr, targetSum) { arr = arr.sort(); var low = 0, high = arr.length - 1, sum, result = []; while(low < high) { sum = arr[low] + arr[high]; if(sum < targetSum) low++; else if(sum > targetSum) high--; else if(sum === targetSum) { result.push({val1: arr[low], val2: arr[high]}); high--; } } return (result || false); } var pairs = findPairOfNumbers([1,2,3,4,5,6,7,8,9,0], 7); if(pairs.length) { console.log(pairs); } else { console.log("No pair of numbers found that sums to " + 7); }
Help with algorithm problem from SPOJ
I thought it would be a fun problem: Prime Path But...It is hard for me. My only idea is "To do something with knapsack problem".. and no other ideas. Could You track me for good way? It's not for any challenge or University homework. I just want to learn something. _ Ok, but firstly, how to find this prime numbers? Do i need to find all 4digit prime numbers, add it to graph? For now i have generating all prime numbers. http://pastebin.com/wbhRNRHQ Could You give me sample code to declare graph build on neighbour list?
Seems like a straightforward graph path finding problem. Take all 4 digit primes as the vertices. Connect two with an edge, if we can go from one to the other. Now given two, you need to find the shortest path between them, in the graph we just formed. A simple BFS (breadth-first-search) should do for that. For programming contests with time limits, you could even hardcode every possible prime pair path and get close to zero running time!
Build a graph where the nodes are all the 4 digit prime numbers, and there are arcs everywhere two numbers have three digits in common. From there, it's a basic graph traversal to find the lowest-cost path from one node to another.
I came across a similar question where I had to convert one 4 digit prime number 1033 to another 4 digit prime number 3739 in minimum number of steps. I was able to solve the problem, it might not be efficient but here is the working code for the same. Below code has been written in Java import java.util.*; public class PrimeNumberProblem { public static void main(String... args) { System.out.println("Minimum number of steps required for converting 1033 to 3739 are = " + getMinSteps(1033, 3739)); } public static int getMinSteps(int a, int b) { if (a == b) return 0; List<Integer> primes = new ArrayList<>(); // get all the 4 digit prime numbers primes = getPrimeNumbers(); // consists of graph with vertices as all the prime numbers Graph graph = addNumbersToGraph(primes); // adding edges to the graph vertices Graph finalGraph = addWeightToGraph(graph); // min number of steps required int result = findShortestRoute(finalGraph.getVertex(a), finalGraph.getVertex(b)); return result; } private static int findShortestRoute(Vertex source, Vertex dest) { if (source.getVertexValue() == dest.getVertexValue()) return 0; // step 1 Initialize the queue. Also Map to store path Queue<Vertex> visitedQueue = new LinkedList<>(); Map<Vertex, Vertex> currentPrevMap = new HashMap<Vertex, Vertex>(); // step 2 start from visiting S (starting node), and mark it visited, add to queue Map<Integer, Boolean> visited = new HashMap<Integer, Boolean>(); visited.put(source.getVertexValue(), true); visitedQueue.add(source); int level = 0; // step 3 Repeat until queue is empty while (!visitedQueue.isEmpty()) { // step 4 remove from queue Vertex current = visitedQueue.remove(); if (current.getVertexValue() == dest.getVertexValue()) { printPath(source, dest, currentPrevMap); return level; } else if (current.getAdjacentVertices().size() > 0) { level++; } // step 5 add each of the unvisited neighbour and mark visited for (Vertex adjacentVertex : current.getAdjacentVertices()) { Integer value = adjacentVertex.getVertexValue(); if (value == dest.getVertexValue()) { currentPrevMap.put(adjacentVertex, current); printPath(source, dest, currentPrevMap); return level; } if (visited.get(value) == null) { currentPrevMap.put(adjacentVertex, current); // mark visited and enqueue it visited.put(value, true); visitedQueue.add(adjacentVertex); } } } // not found System.out.println("Dest vertex not found"); return -1; } private static void printPath(Vertex source, Vertex dest, Map<Vertex, Vertex> currentPrevMap) { Vertex node = dest; System.out.println("Reverse Path from source: " + source.getVertexValue() + " to dest: " + dest.getVertexValue()); while (node != source) { System.out.println(node.getVertexValue()); node = currentPrevMap.get(node); } System.out.println(source.getVertexValue()); } private static Graph addWeightToGraph(Graph graph) { List<Vertex> vertices = graph.getAllVertices(); for (Vertex i : vertices) { for (Vertex j : vertices) { if (i.equals(j)) continue; if (distance(i, j) == 1) { i.getAdjacentVertices().add(j); // i.addEdge(new Edge(i, j, 1)); } } } return graph; } private static int distance(Vertex source, Vertex dest) { if (source.getVertexValue() == dest.getVertexValue()) { return 0; } char[] numA = extractIntegers(source.getVertexValue()); char[] numB = extractIntegers(dest.getVertexValue()); int len1 = numA.length; int tracker = 0; for (int i = 0; i < len1; i++) { if (numA[i] != numB[i]) { numA[i] = numB[i]; tracker++; String sA = String.copyValueOf(numA); String sB = String.copyValueOf(numB); // if we have reached destination if (Integer.parseInt(sA) == Integer.parseInt(sB)) { return tracker; } } } return tracker; } private static char[] extractIntegers(int i) { char[] arr = Integer.toString(i).toCharArray(); return arr; } private static Graph addNumbersToGraph(List<Integer> primes) { Graph g = new Graph(); for (Integer prime : primes) { g.addVertex(new Vertex(prime)); } return g; } private static List<Integer> getPrimeNumbers() { List<Integer> fourDigitPrimes = new ArrayList<>(); fourDigitPrimes.add(1033); fourDigitPrimes.add(1733); fourDigitPrimes.add(3733); fourDigitPrimes.add(3739); // for (int i = 1000; i < 9999; i++) { // if (isPrime(i)) // fourDigitPrimes.add(i); // } return fourDigitPrimes; } private static boolean isPrime(int i) { for (int k = 2; k < Math.sqrt(i); k++) { if (i % k == 0) return false; } return true; } } class Graph { public List<Vertex> vertexList = new ArrayList<Vertex>(); public void addVertex(Vertex V) { vertexList.add(V); } public List getAllAdjacentNodes(Vertex V) { return V.getAdjacentVertices(); } public List getAllVertices() { return vertexList; } public Vertex getVertex(int val) { Iterator<Vertex> keys = vertexList.iterator(); while (keys.hasNext()) { Vertex v = keys.next(); if (v.getVertexValue() == val) return v; } return null; } } class Vertex { int value; private List<Vertex> adjacentVertices = new ArrayList<Vertex>(); public Vertex(int v) { this.value = v; } public List<Vertex> getAdjacentVertices() { return adjacentVertices; } public int getVertexValue() { return value; } #Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Vertex vertex = (Vertex) o; return value == vertex.value; } #Override public int hashCode() { return value; } }
Look into "breadth-first search". Also worth bearing in mind here that the problem can be approached "from both ends" simultaneously (a chain from numbers X to Y can be reversed to get Y to X, and you can exploit this). Precalculating primes will avoid much computation along the way.
I'd run a BFS using probable prime testing, which would work relatively well with only 4 digit numbers. With only 4 digits, also, you may want to use more exacting methods to produce all primes to compare against for faster prime checking.
Could You give me sample code to declare graph build on neighbour list? here is a sample code for breadth first search public static final int MAX = 10000; boolean[] prime = new boolean[MAX]; int[] dist = new int[MAX]; //get digit i [1 to 4] in num public int getDigit(int num,int i){ return num % ((int)Math.pow(10, i)) / ((int) Math.pow(10, i-1)); } //set digit i to d public int setDigit(int num,int i,int d){ return (num - getDigit(num, i)*(int)Math.pow(10, i-1)) + d * (int)Math.pow(10,i-1); } public int bfs(int start,int end){ Queue<Integer> q = new LinkedList<Integer>(); q.add(start); HashSet<Integer> visited = new HashSet<Integer>(); visited.add(start); dist[start] = 0; int x,y,d = 0; while (q.size() > 0){ x = q.poll(); d = dist[x]; if (x == end) return d; for (int i = 1; i < 5; i++) { //digit number i for (int j = 0; j < 10; j++) { //avoid setting last digit if (j == 0 && i == 4) continue; //set digit number i to j y = setDigit(x, i, j); if (prime[y] && y != x && !visited.contains(y)){ q.add(y); visited.add(y); dist[y] = d + 1; } } } } return -1; }
Here is my solution using BFS and I have already saved all 4 digit prime numbers into an array as there is no need to write a function to calculate the prime numbers. I hope it helps #include<stdio.h> int hash[10000]; int a,b,ans,level,new_num,count; int prime[] = {1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601,1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697,1699,1709,1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789,1801,1811,1823,1831,1847,1861,1867,1871,1873,1877,1879,1889,1901,1907,1913,1931,1933,1949,1951,1973,1979,1987,1993,1997,1999,2003,2011,2017,2027,2029,2039,2053,2063,2069,2081,2083,2087,2089,2099,2111,2113,2129,2131,2137,2141,2143,2153,2161,2179,2203,2207,2213,2221,2237,2239,2243,2251,2267,2269,2273,2281,2287,2293,2297,2309,2311,2333,2339,2341,2347,2351,2357,2371,2377,2381,2383,2389,2393,2399,2411,2417,2423,2437,2441,2447,2459,2467,2473,2477,2503,2521,2531,2539,2543,2549,2551,2557,2579,2591,2593,2609,2617,2621,2633,2647,2657,2659,2663,2671,2677,2683,2687,2689,2693,2699,2707,2711,2713,2719,2729,2731,2741,2749,2753,2767,2777,2789,2791,2797,2801,2803,2819,2833,2837,2843,2851,2857,2861,2879,2887,2897,2903,2909,2917,2927,2939,2953,2957,2963,2969,2971,2999,3001,3011,3019,3023,3037,3041,3049,3061,3067,3079,3083,3089,3109,3119,3121,3137,3163,3167,3169,3181,3187,3191,3203,3209,3217,3221,3229,3251,3253,3257,3259,3271,3299,3301,3307,3313,3319,3323,3329,3331,3343,3347,3359,3361,3371,3373,3389,3391,3407,3413,3433,3449,3457,3461,3463,3467,3469,3491,3499,3511,3517,3527,3529,3533,3539,3541,3547,3557,3559,3571,3581,3583,3593,3607,3613,3617,3623,3631,3637,3643,3659,3671,3673,3677,3691,3697,3701,3709,3719,3727,3733,3739,3761,3767,3769,3779,3793,3797,3803,3821,3823,3833,3847,3851,3853,3863,3877,3881,3889,3907,3911,3917,3919,3923,3929,3931,3943,3947,3967,3989,4001,4003,4007,4013,4019,4021,4027,4049,4051,4057,4073,4079,4091,4093,4099,4111,4127,4129,4133,4139,4153,4157,4159,4177,4201,4211,4217,4219,4229,4231,4241,4243,4253,4259,4261,4271,4273,4283,4289,4297,4327,4337,4339,4349,4357,4363,4373,4391,4397,4409,4421,4423,4441,4447,4451,4457,4463,4481,4483,4493,4507,4513,4517,4519,4523,4547,4549,4561,4567,4583,4591,4597,4603,4621,4637,4639,4643,4649,4651,4657,4663,4673,4679,4691,4703,4721,4723,4729,4733,4751,4759,4783,4787,4789,4793,4799,4801,4813,4817,4831,4861,4871,4877,4889,4903,4909,4919,4931,4933,4937,4943,4951,4957,4967,4969,4973,4987,4993,4999,5003,5009,5011,5021,5023,5039,5051,5059,5077,5081,5087,5099,5101,5107,5113,5119,5147,5153,5167,5171,5179,5189,5197,5209,5227,5231,5233,5237,5261,5273,5279,5281,5297,5303,5309,5323,5333,5347,5351,5381,5387,5393,5399,5407,5413,5417,5419,5431,5437,5441,5443,5449,5471,5477,5479,5483,5501,5503,5507,5519,5521,5527,5531,5557,5563,5569,5573,5581,5591,5623,5639,5641,5647,5651,5653,5657,5659,5669,5683,5689,5693,5701,5711,5717,5737,5741,5743,5749,5779,5783,5791,5801,5807,5813,5821,5827,5839,5843,5849,5851,5857,5861,5867,5869,5879,5881,5897,5903,5923,5927,5939,5953,5981,5987,6007,6011,6029,6037,6043,6047,6053,6067,6073,6079,6089,6091,6101,6113,6121,6131,6133,6143,6151,6163,6173,6197,6199,6203,6211,6217,6221,6229,6247,6257,6263,6269,6271,6277,6287,6299,6301,6311,6317,6323,6329,6337,6343,6353,6359,6361,6367,6373,6379,6389,6397,6421,6427,6449,6451,6469,6473,6481,6491,6521,6529,6547,6551,6553,6563,6569,6571,6577,6581,6599,6607,6619,6637,6653,6659,6661,6673,6679,6689,6691,6701,6703,6709,6719,6733,6737,6761,6763,6779,6781,6791,6793,6803,6823,6827,6829,6833,6841,6857,6863,6869,6871,6883,6899,6907,6911,6917,6947,6949,6959,6961,6967,6971,6977,6983,6991,6997,7001,7013,7019,7027,7039,7043,7057,7069,7079,7103,7109,7121,7127,7129,7151,7159,7177,7187,7193,7207,7211,7213,7219,7229,7237,7243,7247,7253,7283,7297,7307,7309,7321,7331,7333,7349,7351,7369,7393,7411,7417,7433,7451,7457,7459,7477,7481,7487,7489,7499,7507,7517,7523,7529,7537,7541,7547,7549,7559,7561,7573,7577,7583,7589,7591,7603,7607,7621,7639,7643,7649,7669,7673,7681,7687,7691,7699,7703,7717,7723,7727,7741,7753,7757,7759,7789,7793,7817,7823,7829,7841,7853,7867,7873,7877,7879,7883,7901,7907,7919,7927,7933,7937,7949,7951,7963,7993,8009,8011,8017,8039,8053,8059,8069,8081,8087,8089,8093,8101,8111,8117,8123,8147,8161,8167,8171,8179,8191,8209,8219,8221,8231,8233,8237,8243,8263,8269,8273,8287,8291,8293,8297,8311,8317,8329,8353,8363,8369,8377,8387,8389,8419,8423,8429,8431,8443,8447,8461,8467,8501,8513,8521,8527,8537,8539,8543,8563,8573,8581,8597,8599,8609,8623,8627,8629,8641,8647,8663,8669,8677,8681,8689,8693,8699,8707,8713,8719,8731,8737,8741,8747,8753,8761,8779,8783,8803,8807,8819,8821,8831,8837,8839,8849,8861,8863,8867,8887,8893,8923,8929,8933,8941,8951,8963,8969,8971,8999,9001,9007,9011,9013,9029,9041,9043,9049,9059,9067,9091,9103,9109,9127,9133,9137,9151,9157,9161,9173,9181,9187,9199,9203,9209,9221,9227,9239,9241,9257,9277,9281,9283,9293,9311,9319,9323,9337,9341,9343,9349,9371,9377,9391,9397,9403,9413,9419,9421,9431,9433,9437,9439,9461,9463,9467,9473,9479,9491,9497,9511,9521,9533,9539,9547,9551,9587,9601,9613,9619,9623,9629,9631,9643,9649,9661,9677,9679,9689,9697,9719,9721,9733,9739,9743,9749,9767,9769,9781,9787,9791,9803,9811,9817,9829,9833,9839,9851,9857,9859,9871,9883,9887,9901,9907,9923,9929,9931,9941,9949,9967,9973}; int size = sizeof(prime)/sizeof(prime[0]); int bfs(int,int); typedef struct q{ int x, c; } queue; queue qq[10000]; int isprime(int x) { int l,r,m; l=m=0; r=size-1; while (l <= r) { int m = l + (r-l)/2; if (prime[m] == x) return 1; if (prime[m] < x) l = m + 1; else r = m - 1; } return 0; } int bfs(int num1,int num2) { int i,j,k,p,q,n; new_num=p=q=0; i=0; j=1; qq[i].x = num1; qq[i].c = 0; hash[num1] = 1; while(i!=j) { n = qq[i].x; level = qq[i].c; if(n==num2) { count = level; return count; } level++; p = n%1000; for(k=1;k<10;k++) { new_num = (k*1000)+ p; if(isprime(new_num)&&(new_num!=n)&&(!hash[new_num])) { hash[new_num] = 1; qq[j].x = new_num; qq[j].c = level; j++; }} p=q=new_num=0; p = n/1000; q = n%100; for(k=0;k<10;k++) { new_num = (p*1000)+k*100+q; if(isprime(new_num)&&(new_num!=n)&&(!hash[new_num])) { hash[new_num] = 1; qq[j].x = new_num; qq[j].c = level; j++; }} p=q=new_num=0; p = n/100; q = n%10; for(k=0;k<10;k++) { new_num = (p*100)+k*10+q; if(isprime(new_num)&&(new_num!=n)&&(!hash[new_num])) { hash[new_num] = 1; qq[j].x = new_num; qq[j].c = level; j++; }} p=q=new_num=0; p = n/10; for(k=0;k<10;k++) { new_num = (p*10)+k; if(isprime(new_num)&&(new_num!=n)&&(!hash[new_num])) { hash[new_num] = 1; qq[j].x = new_num; qq[j].c = level; j++; }} p=q=new_num=0; i++; } return -1;} int main() { int v,tc; setbuf(stdout,NULL); scanf("%d",&tc); for(v=1;v<=tc;v++) { int i,j; a=b=ans=level=new_num=count=0; for(i=0;i<10000;i++) {qq[i].x=0; qq[i].c=0; hash[i]=0;} scanf("%d%d",&a,&b); if(a==b) { ans = 0;} else { ans = bfs(a,b);} printf("Case #%d\n", v); if(ans==-1) { printf("Impossible\n"); } else {printf("%d\n",ans);} } return 0; }
My Python solution using BFS: import queue # Class to represent a graph class Graph: def __init__(self, V): self.V = V # No. of vertices self.prime_list = [[] for i in range(V)] # function to add an edge to graph def addedge(self, V1, V2): self.prime_list[V1].append(V2) self.prime_list[V2].append(V1) def bfs(self, in1, in2): visited = [0] * self.V que = queue.Queue() visited[in1] = 1 que.put(in1) while not que.empty(): prime_index = que.get() i = 0 while i < len(self.prime_list[prime_index]): if not visited[self.prime_list[prime_index][i]]: visited[self.prime_list[prime_index][i]] = visited[prime_index] + 1 que.put(self.prime_list[prime_index][i]) if self.prime_list[prime_index][i] == in2: return visited[self.prime_list[prime_index][i]] - 1 i += 1 # // Finding all 4 digit prime numbers def SieveOfEratosthenes(v): # Create a boolean array "prime[0..n]" and initialize all entries it as true. A value in prime[i] will be # finally be false if i is Not a prime, else true. n = 9999 prime = [True] * (n + 1) p = 2 while p * p <= 9999: if prime[p]: i = p * p while i <= 9999: prime[i] = False i = i + p p = p + 1 # v = [] for i in range(1000, n + 1): if prime[i]: v.append(i) return v def compare(a, b): diff = 0 while a: if a % 10 != b % 10: diff += 1 a //= 10 b //= 10 # If the numbers differ only by a single # digit return true else false if diff > 1: return False return True def shortestPath(num1, num2): # Generate all 4 digit pset = [] SieveOfEratosthenes(pset) # Create a graph where node numbers # are indexes in pset[] and there is # an edge between two nodes only if # they differ by single digit. g = Graph(len(pset)) for i in range(len(pset)): for j in range(i + 1, len(pset)): if compare(pset[i], pset[j]): g.addedge(i, j) # Since graph nodes represent indexes # of numbers in pset[], we find indexes of num1 and num2. in1, in2 = None, None for j in range(len(pset)): if pset[j] == num1: in1 = j for j in range(len(pset)): if pset[j] == num2: in2 = j return g.bfs(in1, in2) # Driver code if __name__ == '__main__': num1 = 1033 num2 = 8179 print(shortestPath(num1, num2))
Minimum window width in string x that contains all characters of string y
Find minimum window width in string x that contains all characters of another string y. For example: String x = "coobdafceeaxab" String y = "abc" The answer should be 5, because the shortest substring in x that contains all three letters of y is "bdafc". I can think of a naive solution with complexity O(n^2 * log(m)), where n = len(x) and m = len(y). Can anyone suggest a better solution? Thanks. Update: now think of it, if I change my set to tr1::unordered_map, then I can cut the complexity down to O(n^2), because insertion and deletion should both be O(1).
time: O(n) (One pass) space: O(k) This is how I would do it: Create a hash table for all the characters from string Y. (I assume all characters are different in Y). First pass: Start from first character of string X. update hash table, for exa: for key 'a' enter location (say 1). Keep on doing it until you get all characters from Y (until all key in hash table has value). If you get some character again, update its newer value and erase older one. Once you have first pass, take smallest value from hash table and biggest value. Thats the minimum window observed so far. Now, go to next character in string X, update hash table and see if you get smaller window. Edit: Lets take an example here: String x = "coobdafceeaxab" String y = "abc" First initialize a hash table from characters of Y. h[a] = -1 h[b] = -1 h[c] = -1 Now, Start from first character of X. First character is c, h[c] = 0 Second character (o) is not part of hash, skip it. .. Fourth character (b), h[b] = 3 .. Sixth character(a), enter hash table h[a] = 5. Now, all keys from hash table has some value. Smallest value is 0 (of c) and highest value is 5 (of a), minimum window so far is 6 (0 to 5). First pass is done. Take next character. f is not part of hash table, skip it. Next character (c), update hash table h[c] = 7. Find new window, smallest value is 3 (of b) and highest value is 7 (of c). New window is 3 to 7 => 5. Keep on doing it till last character of string X. I hope its clear now. Edit There are some concerns about finding max and min value from hash. We can maintain sorted Link-list and map it with hash table. Whenever any element from Link list changes, it should be re-mapped to hash table. Both these operation are O(1) Total space would be m+m Edit Here is small visualisation of above problem: For "coobdafceeaxab" and "abc" step-0: Initial doubly linked-list = NULL Initial hash-table = NULL step-1: Head<->[c,0]<->tail h[c] = [0, 'pointer to c node in LL'] step-2: Head<->[c,0]<->[b,3]<->tail h[c] = [0, 'pointer to c node in LL'], h[b] = [3, 'pointer to b node in LL'], Step-3: Head<->[c,0]<->[b,3]<->[a,5]<->tail h[c] = [0, 'pointer to c node in LL'], h[b] = [3, 'pointer to b node in LL'], h[a] = [5, 'pointer to a node in LL'] Minimum Window => difference from tail and head => (5-0)+1 => Length: 6 Step-4: Update entry of C to index 7 here. (Remove 'c' node from linked-list and append at the tail) Head<->[b,3]<->[a,5]<->[c,7]<->tail h[c] = [7, 'new pointer to c node in LL'], h[b] = [3, 'pointer to b node in LL'], h[a] = [5, 'pointer to a node in LL'], Minimum Window => difference from tail and head => (7-3)+1 => Length: 5 And so on.. Note that above Linked-list update and hash table update are both O(1). Please correct me if I am wrong.. Summary: TIme complexity: O(n) with one pass Space Complexity: O(k) where k is length of string Y
I found this very nice O(N) time complexity version here http://leetcode.com/2010/11/finding-minimum-window-in-s-which.html, and shortened it slightly (removed continue in a first while , which allowed to simplify condition for the second while loop). Note, that this solution allows for duplicates in the second string, while many of the above answers do not. private static String minWindow(String s, String t) { int[] needToFind = new int[256]; int[] hasFound = new int[256]; for(int i = 0; i < t.length(); ++i) { needToFind[t.charAt(i)]++; } int count = 0; int minWindowSize = Integer.MAX_VALUE; int start = 0, end = -1; String window = ""; while (++end < s.length()) { char c = s.charAt(end); if(++hasFound[c] <= needToFind[c]) { count++; } if(count < t.length()) continue; while (hasFound[s.charAt(start)] > needToFind[s.charAt(start)]) { hasFound[s.charAt(start++)]--; } if(end - start + 1 < minWindowSize) { minWindowSize = end - start + 1; window = s.substring(start, end + 1); } } return window; }
Here's my solution in C++: int min_width(const string& x, const set<char>& y) { vector<int> at; for (int i = 0; i < x.length(); i++) if (y.count(x[i]) > 0) at.push_back(i); int ret = x.size(); int start = 0; map<char, int> count; for (int end = 0; end < at.size(); end++) { count[x[at[end]]]++; while (count[x[at[start]]] > 1) count[x[at[start++]]]--; if (count.size() == y.size() && ret > at[end] - at[start] + 1) ret = at[end] - at[start] + 1; } return ret; } Edit: Here's an implementation of Jack's idea. It's the same time complexity as mine, but without the inner loop that confuses you. int min_width(const string& x, const set<char>& y) { int ret = x.size(); map<char, int> index; set<int> index_set; for (int j = 0; j < x.size(); j++) { if (y.count(x[j]) > 0) { if (index.count(x[j]) > 0) index_set.erase(index[x[j]]); index_set.insert(j); index[x[j]] = j; if (index.size() == y.size()) { int i = *index_set.begin(); if (ret > j-i+1) ret = j-i+1; } } } return ret; } In Java it can be implemented nicely with LinkedHashMap: static int minWidth(String x, HashSet<Character> y) { int ret = x.length(); Map<Character, Integer> index = new LinkedHashMap<Character, Integer>(); for (int j = 0; j < x.length(); j++) { char ch = x.charAt(j); if (y.contains(ch)) { index.remove(ch); index.put(ch, j); if (index.size() == y.size()) { int i = index.values().iterator().next(); if (ret > j - i + 1) ret = j - i + 1; } } } return ret; } All operations inside the loop take constant time (assuming hashed elements disperse properly).
There is an O(n solution to this problem). It very well described in this article. http://www.leetcode.com/2010/11/finding-minimum-window-in-s-which.html Hope it helps.
This is my solution in C++, just for reference. Update: originally I used std::set, now I change it to tr1::unordered_map to cut complexity down to n^2, otherwise these two implementations look pretty similar, to prevent this post from getting too long, I only list the improved solution. #include <iostream> #include <tr1/unordered_map> #include <string> using namespace std; using namespace std::tr1; typedef tr1::unordered_map<char, int> hash_t; // Returns min substring width in which sentence contains all chars in word // Returns sentence's length + 1 if not found size_t get_min_width(const string &sent, const string &word) { size_t min_size = sent.size() + 1; hash_t char_set; // char set that word contains for (size_t i = 0; i < word.size(); i++) { char_set.insert(hash_t::value_type(word[i], 1)); } for (size_t i = 0; i < sent.size() - word.size(); i++) { hash_t s = char_set; for (size_t j = i; j < min(j + min_size, sent.size()); j++) { s.erase(sent[j]); if (s.empty()) { size_t size = j - i + 1; if (size < min_size) min_size = size; break; } } } return min_size; } int main() { const string x = "coobdafceeaxab"; const string y = "abc"; cout << get_min_width(x, y) << "\n"; }
An implementation of Jack's idea. public int smallestWindow(String str1, String str2){ if(str1==null || str2==null){ throw new IllegalArgumentException(); } Map<String, Node> map=new HashMap<String, Node>(); Node head=null, current=null; for(int i=0;i<str1.length();i++){ char c=str1.charAt(i); if(head==null){ head=new Node(c); current=head; map.put(String.valueOf(c), head); } else{ current.next=new Node(c); current.next.pre=current; current=current.next; map.put(String.valueOf(c), current); } } Node end=current; int min=Integer.MAX_VALUE; int count=0; for(int i=0;i<str2.length();i++){ char c = str2.charAt(i); Node n=map.get(String.valueOf(c)); if(n!=null){ if(n.index==Integer.MAX_VALUE){ count++; } n.index=i; if(n==head){ Node temp=head; head=head.next; if(head==null){//one node return 1; } head.pre=null; temp.pre=end; end.next=temp; temp.next=null; end=temp; } else if(end!=n){ n.pre.next=n.next; n.next.pre=n.pre; n.pre=end; n.next=null; end.next=n; end=n; } if(count==str1.length()){ min=Math.min(end.index-head.index+1, min); } } } System.out.println(map); return min; }
Simple java solution using the sliding window. Extending NitishMD's idea above: public class StringSearchDemo { public String getSmallestSubsetOfStringContaingSearchString(String toMatch, String inputString) { if (inputString.isEmpty() || toMatch.isEmpty()) { return null; } // List<String> results = new ArrayList<String>(); // optional you can comment this out String smallestMatch = ""; // String largestMatch = ""; int startPointer = 0, endPointer = 1; HashMap<Character, Integer> toMatchMap = new HashMap<Character, Integer>(); for (char c : toMatch.toCharArray()) { if (toMatchMap.containsKey(c)) { toMatchMap.put(c, (toMatchMap.get(c) + 1)); } else { toMatchMap.put(c, 1); } } int totalCount = getCountofMatchingString(toMatchMap, toMatch); for (int i = 0; i < inputString.length();) { if (!toMatchMap.containsKey(inputString.charAt(i))) { endPointer++; i++; continue; } String currentSubString = inputString.substring(startPointer, endPointer); if (getCountofMatchingString(toMatchMap, currentSubString) >= totalCount) { // results.add(currentSubString); // optional you can comment this out if (smallestMatch.length() > currentSubString.length()) { smallestMatch = currentSubString; } else if (smallestMatch.isEmpty()) { smallestMatch = currentSubString; } // if (largestMatch.length() < currentSubString.length()) { // largestMatch = currentSubString; // } startPointer++; } else { endPointer++; i++; } } // System.out.println("all possible combinations = " + results); // optional, you can comment this out // System.out.println("smallest result = " + smallestMatch); // System.out.println("largest result = " + largestMatch); return smallestMatch; } public int getCountofMatchingString(HashMap<Character, Integer> toMatchMap, String toMatch) { int match = 0; HashMap<Character, Integer> localMap = new HashMap<Character, Integer>(); for (char c : toMatch.toCharArray()) { if (toMatchMap.containsKey(c)) { if (localMap.containsKey(c)) { if (localMap.get(c) < toMatchMap.get(c)) { localMap.put(c, (localMap.get(c) + 1)); match++; } } else { localMap.put(c, 1); match++; } } } return match; } public static void main(String[] args) { String inputString = "zxaddbddxyy由ccbbwwaay漢字由来"; String matchCriteria = "a由"; System.out.println("input=" + matchCriteria); System.out.println("matchCriteria=" + inputString); String result = (new StringSearchDemo()) .getSmallestSubsetOfStringContaingSearchString(matchCriteria, inputString); System.out.println("smallest possbile match = " + result); } }