I am absolutely a noob on python, and just started to practice on leetcode. Anyway take a look at this TwoSum exercise: Given an array of integers, find two numbers such that they add up to a specific target number.
Here is my code for this exercise:
class Solution(object):
def __init__(self, nums, target):
self.nums = nums
self.target = target
def twoSum(self):
for i in range(len(self.nums)):
for j in range(i+1, len(self.nums)):
if self.nums[i] + self.nums[j] == self.target:
print "index1=" + str(i) + ", index2=" + str(j)
sample = Solution([2, 8, 7, 15], 9)
sample.twoSum()
Anyone can help me how should the leetcode algorithm answer be look like? Will this one be OK for an interview? Thanks
I wouldn't consider your code or the itertools solution acceptable, because they are both O(n^2). If given in an interview, the interviewer probably wants to see that you can do better than just running two nested for loops.
I would use a hash table or sort the array and then binary search the answer.
Hash table pseudocode
h = empty hash table
for each element e in array
if target - e in hash table:
we have a solution
add e to hash table
This will have complexity O(n), subject to some hash table quirks: in the worst case, it can be O(n^2), but that shouldn't happen for random inputs.
Binary search pseudocode
sort array
for each element e in array
binary search for target - e, make sure it's not found at the same index
if it is, check the before / after element
or think how you can avoid this happening
This will always be O(n log n).
If complexity doesn't matter, then the itertools solution is nice, but your code also gets the job done.
This code is acceptable in an interview, but in real life you should learn to know the libraries. In this example it's itertools.combinations:
from itertools import combinations
for item in combinations([2, 8, 7, 15], 2):
if sum(item) == 9:
print item # prints (2, 7)
Brute Force (Naive), Time Complexity O(n^2):
class Solution:
def twoSum(self, nums, target):
for i in range(0, len(nums)):
to_find = target-nums[i]
for j in range(0, len(nums)):
if j!=i and nums[j] == to_find:
return [i, j]
return [-1, -1]
Using Sorting, Time Complexity O(nlogn):
class Solution:
def twoSum(self, nums, target):
nums = sorted(nums)
for i in range(len(nums)):
to_find = target - nums[i]
left, ryt = 0, len(nums)-1
while left<ryt:
mid = (left+ryt)//2
if mid != i and nums[mid] == to_find:
return [i, mid]
elif nums[mid]>to_find:
ryt = mid-1
else:
left = mid+1
return [-1, -1]
Using Sorting with Two Pointer Approach, Time Complexity O(nlogn):
Improved version of above sorting approach but still Time Complexity is O(nlogn)
Using Hashmap, Time Complexity O(n):
class Solution:
def twoSum(self, nums, target):
num_to_idx = {}
for i, num in enumerate(nums):
if target-num in num_to_idx:
return [i, num_to_idx[target-num]]
num_to_idx[num] = i
return [-1, -1]
Your solution used nested loop which may cause timeout error. You can use dictionary to optimize performance.
This is my solution:
class Solution:
def twoSum(self, num, target):
map = {}
for i in range(len(num)):
if num[i] in map:
return map[num[i]], i
else:
map[target - num[i]] = i
return -1, -1
What's more, you should never modify public method signature.
use hash table is the easiest solution:
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
d = {}
for i, n in enumerate(nums):
if n in d:
return [d[n], i]
d[target - n] = i
enjoy
The thing is many interviewers ask to solve the problem in O(n) time complexity.
Here is a tip:- if interviewers ask you to reduce time complexity from O(n^2) to O(n) or O(n^3) to O(n^2) you can guess that you have to use hash table in such case for 60% of the time. You can easily solve the twoSum problem using hash table (in python it is called dictionary) to solve it. Here is my solution of the problem in python which is 94% faster than accepted ones:-
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
l = len(nums)
my_dic = {}
for i in range(l):
t = target - nums[i]
v=my_dic.get(t,-1)
if v!=-1:
if my_dic[t]!=i:
return [my_dic[t],i]
else:
my_dic[nums[i]] = i
return []
You can ask me any question if you don't understand the solution
I have used a dictionary to store value/index as searching for a key in a dictionary can be done in O(1).
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dict_nums = dict()
for index, value in enumerate(nums):
reminder = target - value
if reminder in dict_nums:
return [dict_nums[reminder], index]
dict_nums[value] = index
Time complexity: O(n)
Space complexity: O(n)
a = {}
i = 0
while i < len(nums):
if nums[i] not in a.values():
a[i] = target - nums[i]
else:
keys = list(a.keys())
vals = list(a.values())
key = keys[vals.index(nums[i])]
return [i,key]
i += 1
return
Using a dictionary you could solve in better time complexity
There were Binary Search suggestions mentioned in other answers, but there was no implementation for them. So I decided to do this.
First I sort array of numbers and then do a binary search. For each number n I binary-search for target - n in sorted array.
Overall time complexity is O(N * Log(N)).
Try it online!
def two_sums(nums, target):
def binary_search(f, a, b):
while a < b:
m = (a + b) // 2
if f(m):
b = m
else:
a = m + 1
assert a == b and f(a), (a, b, f(a))
return a
nums = sorted(enumerate(nums), key = lambda e: e[1])
for i, n in nums:
begin, end = [
binary_search(lambda j: j >= len(nums) or
fcmp(nums[j][1], target - n), 0, len(nums))
for fcmp in [lambda a, b: a >= b, lambda a, b: a > b]
]
for j in range(begin, end):
if nums[j][0] != i:
return sorted([nums[j][0], i])
print(two_sums([2, 8, 7, 15], 9))
Output:
[0, 2]
C++
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> numToIndex;
for (int i = 0; i < nums.size(); ++i) {
if (numToIndex.count(target - nums[i]))
return {numToIndex[target - nums[i]], i};
numToIndex[nums[i]] = i;
}
throw;
}
};
C++
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int, int> umap;
int difference;
for(int i = 0; i < nums.size(); i++ ){
difference = target - nums.at(i);
if(umap.find(difference) != umap.end()) {
vector<int> v{umap[difference], i};
return v;
} else {
umap[nums.at(i)] = i;
}
}
return vector<int> {};
}
};
C++
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> m;
vector<int> result;
for (int i=0; i<nums.size(); i++) {
if ( m.find(target - nums[i]) == m.end() ) {
m[nums[i]] = i;
}else{
result.push_back(m[target - nums[i]]);
result.push_back(i);
}
}
return result;
}
};
C++
class Solution {
public:
vector<int> twoSum(vector<int> &nums, int target) {
unordered_map<int, int> index_map;
for (int index = 0;; index++) {
auto iter = index_map.find(target - nums[index]);
if (iter != index_map.end())
return vector<int> {index, iter -> second};
index_map[nums[index]] = index;
}
}
};
C
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
*returnSize=2;
int *arr=(int *)malloc((*returnSize)*sizeof(int));
for(int i=0;i<numsSize;i++){
for(int j=i+1;j<numsSize;j++){
if(nums[i]+nums[j]==target){
arr[0]=i;
arr[1]=j;
break;
}
}
}
return arr;
}
JAVA
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> numToIndex = new HashMap<>();
for (int i = 0; i < nums.length; ++i) {
if (numToIndex.containsKey(target - nums[i]))
return new int[] {numToIndex.get(target - nums[i]), i};
numToIndex.put(nums[i], i);
}
throw new IllegalArgumentException();
}
}
JAVA
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] indices = new int[2];
Map<Integer, Integer> map = new HashMap<>();
for (int index = 0; index < nums.length; index++) {
if (map.containsKey(target - nums[index])) {
indices[1] = index;
indices[0] = map.get(target - nums[index]);
return indices;
}
map.put(nums[index], index);
}
return indices;
}
}
JAVA
class Solution {
public int[] twoSum(int[] nums, int target) {
if(nums==null || nums.length<2)
return new int[]{0,0};
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i=0; i<nums.length; i++){
if(map.containsKey(nums[i])){
return new int[]{map.get(nums[i]), i};
}else{
map.put(target-nums[i], i);
}
}
return new int[]{0,0};
}
}
Python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
x = len(nums)
for i in range(x-1):
for j in range(1,x):
if i == j:
continue
if nums[i] + nums[j] == target:
return [i,j]
Python
class Solution:
def twoSum(self, nums, target):
length = len(nums)
for i in range(length):
for j in range(i + 1, length):
if nums[i] + nums[j] == target:
return [i, j]
Python
class Solution:
def twoSum(self, nums, target):
index_map = {}
for index, num in enumerate(nums):
if target - num in index_map:
return index_map[target - num], index
index_map[num] = index
Python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
numToIndex = {}
for i, num in enumerate(nums):
if target - num in numToIndex:
return numToIndex[target - num], i
numToIndex[num] = i
javascript
/**
* #param {number[]} nums
* #param {number} target
* #return {number[]}
*/
var twoSum = function (nums, target) {
// Array to store the result
result = [];
// Map to store the difference and its index
index_map = new Map();
// Loop for each element in the array
for (let i = 0; i < nums.length; i++) {
let difference = target - nums[i];
if (index_map.has(difference)) {
result[0] = i;
result[1] = index_map.get(difference);
break;
} else {
index_map.set(nums[i], i);
}
}
return result;
};
GOlang
func twoSum(nums []int, target int) []int {
record := make(map[int]int)
for index, num := range nums {
difference := target - num
if res, ok := record[difference]; ok {
return []int{index, res}
}
record[num] = index
}
return []int{}
}
Kotlin
class Solution {
fun twoSum(nums: IntArray, target: Int): IntArray {
// Array to store result
val result = IntArray(2)
// This map will store the difference and the corresponding index
val map: MutableMap<Int, Int> = HashMap()
// Loop through the entire array
for (i in nums.indices) {
// If we have seen the current element before
// It means we have already encountered the other number of the pair
if (map.containsKey(nums[i])) {
// Index of the current element
result[0] = i
// Index of the other element of the pair
result[1] = map[nums[i]]!!
break
} else {
// Save the difference of the target and the current element
// with the index of the current element
map[target - nums[i]] = i
}
}
return result
}
}
You can ask me any question if you don't understand the solution
Related
I'm trying to convert a Python algorithm from this Stack Overflow answer to split a string without spaces into words to C#.
Unfortunately I don't know anything about Python so the translation is proving very difficult.
The lines I don't understand are:
wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words)) <= THIS LINE
and
def best_match(i):
candidates = enumerate(reversed(cost[max(0, i-maxword):i]))
return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates) <= THIS LINE
It looks as though best_match(i) it should return a Tuple<>. What is the equivalent in C#?
Here is the full Python script:
from math import log
# Build a cost dictionary, assuming Zipf's law and cost = -math.log(probability).
words = open("words-by-frequency.txt").read().split()
wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words))
maxword = max(len(x) for x in words)
def infer_spaces(s):
"""Uses dynamic programming to infer the location of spaces in a string
without spaces."""
# Find the best match for the i first characters, assuming cost has
# been built for the i-1 first characters.
# Returns a pair (match_cost, match_length).
def best_match(i):
candidates = enumerate(reversed(cost[max(0, i-maxword):i]))
return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates)
# Build the cost array.
cost = [0]
for i in range(1,len(s)+1):
c,k = best_match(i)
cost.append(c)
# Backtrack to recover the minimal-cost string.
out = []
i = len(s)
while i>0:
c,k = best_match(i)
assert c == cost[i]
out.append(s[i-k:i])
i -= k
return " ".join(reversed(out))
I found that algorithm interesting so here is my translation:
class WordSplitter {
private readonly Dictionary<string, double> _wordCosts;
private readonly int _maxWordLength;
public WordSplitter(string freqFilePath) {
// words = open("words-by-frequency.txt").read().split()
var words = File.ReadAllLines(freqFilePath);
// wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words))
_wordCosts = words.Select((k, i) => new { Key = k, Value = Math.Log((i + 1) * Math.Log(words.Length)) }).ToDictionary(c => c.Key, c => c.Value);
// maxword = max(len(x) for x in words)
_maxWordLength = words.Select(c => c.Length).Max();
}
public string InferSpaces(string target) {
// cost = [0]
var costs = new List<double>() { 0 };
foreach (var i in Enumerable.Range(1, target.Length)) {
var (c, k) = BestMatch(i);
costs.Add(c);
}
var output = new List<string>();
int len = target.Length;
while (len > 0) {
var (c, k) = BestMatch(len);
Debug.Assert(k > 0);
Debug.Assert(c == costs[len]);
// use Substring if your compiler version doesn't support slicing
// but pay attention that Substring second argument is length, not end index
output.Add(target[(len - k)..len]);
len -= k;
}
output.Reverse();
return String.Join(" ", output);
(double cost, int length) BestMatch(int i) {
var start = Math.Max(0, i - _maxWordLength);
// GetRange second argument is length
var x = costs.GetRange(start, i - start);
x.Reverse();
// now, this part is easier to comprehend if it's expanded a bit
// you can do it in cryptic way too like in python though if you like
(double cost, int length)? result = null;
for (int k = 0; k < x.Count; k++) {
var c = x[k];
var sub = target[(i - k - 1)..i];
var cost = c + (_wordCosts.ContainsKey(sub) ? _wordCosts[sub] : 9e99); // 9e99 is just some big number. 9e999 is outside of double range in C#, so use smaller one
// save minimal cost
if (result == null || result.Value.cost > cost)
result = (cost, k + 1);
}
// return minimal cost
return result.Value;
}
}
}
Usage:
var splitter = new WordSplitter(#"C:\tmp\words.txt");
var result = splitter.InferSpaces("thumbgreenappleactiveassignmentweeklymetaphor");
Recently HackerRank launched their own certifications. Among the tests they offer is "Problem Solving". The test contains 2 problems; they give you 90 minutes to solve them. Being inexperienced as I am, I failed, because it took me longer than that.
Specifically, I came up with the solution for the first problem (filled orders, see below) in, like 30 minutes, and spent the rest of the time trying to debugg it. The problem with it wasn't that the solution didn't work, but that it worked on only some of the test cases.
Out of 14 testcases the solution worked on 7 (including all the open ones and a bunch of closed ones), and didn't work on the remaining 7 (all closed). Closed means that the input data is not available, as well as expected output. (Which makes sense, because some of the lists there included 250K+ elements.)
But it drives me crazy; I can't figure out what might be wrong with it. I tried putting print statements all over the place, but the only thing I came to is that 1 too many elements get added to the list - hence, the last if statement (to drop the last added element), but it made no difference whatsoever, so it's probably wrong.
Here's the problem:
A widget manufacturer is facing unexpectedly high demand for its new product,. They would like to satisfy as many customers as possible. Given a number of widgets available and a list of customer orders, what is the maximum number of orders the manufacturer can fulfill in full?
Function Description
Complete the function filledOrders in the editor below. The function must return a single integer denoting the maximum possible number of fulfilled orders.
filledOrders has the following parameter(s):
order : an array of integers listing the orders
k : an integer denoting widgets available for shipment
Constraints
1 ≤ n ≤ 2 x 105
1 ≤ order[i] ≤ 109
1 ≤ k ≤ 109
Sample Input For Custom Testing
2
10
30
40
Sample Output
2
And here's my function:
def filledOrders(order, k):
total = k
fulf = []
for r in order:
if r <= total:
fulf.append(r)
total -= r
else:
break
if sum(fulf) > k:
fulf.pop()
return len(fulf)
Java Solution
int count = 0;
Collections.sort(order);
for(int i=0; i<order.size(); i++) {
if(order.get(i)<=k) {
count++;
k = k - order.get(i);
}
}
return count;
Code Revision
def filledOrders(order, k):
total = 0
for i, v in enumerate(sorted(order)):
if total + v <= k:
total += v # total stays <= k
else:
return i # provides the count
else:
return len(order) # was able to place all orders
print(filledOrders([3, 2, 1], 3)) # Out: 2
print(filledOrders([3, 2, 1], 1)) # Out: 1
print(filledOrders([3, 2, 1], 10)) # Out: 3
print(filledOrders([3, 2, 1], 0)) # Out: 0
Advanced Javascript solution :
function filledOrders(order, k) {
// Write your code here
let count = 0; let total=0;
const ordersLength = order.length;
const sortedOrders = order.sort(function(a,b) {
return (+a) - (+b);
});
for (let i = 0; i < ordersLength; i++) {
if (total + sortedOrders[i] <= k) {
// if all orders able to be filled
if (total <= k && i === ordersLength - 1) return ordersLength;
total += sortedOrders[i];
count++;
} else {
return count;
}
}
}
Python code
def filledOrders(order, k):
orderfulfilled=0
for i in range(1,len(order)):
m=k-order[i]
if(m>=0):
orderfulfilled+=1
k-=order[i]
return(orderfulfilled)
Javascript solution
Option1:
function filledOrders(order, k) {
let count=0;
let arr= [];
arr = order.sort().filter((item, index) => {
if (item<=k) {
k = k - item;
return item
}
})
return arr.length
}
Option2:
function filledOrders(order, k) {
let count=0;
for(var i=0; i<order.sort().length; i++) {
if(order[i]<=k) {
count++;
k = k - order[i]
}
}
return count;
}
C#
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using System.Text;
using System;
using System.Reflection.Metadata.Ecma335;
class Result
{
/*
* Complete the 'filledOrders' function below.
*
* The function is expected to return an INTEGER.
* The function accepts following parameters:
* 1. INTEGER_ARRAY order
* 2. INTEGER k
*/
public static int filledOrders(List<int> order, int k)
{
if (order.Sum() <= k)
{
return order.Count();
}
else
{
int counter = 0;
foreach (int element in order)
{
if (element <= k)
{
counter++;
k = k - element;
}
}
return counter;
}
}
}
class Solution
{
public static void Main(string[] args)
{
int orderCount = Convert.ToInt32(Console.ReadLine().Trim());
List<int> order = new List<int>();
for (int i = 0; i < orderCount; i++)
{
int orderItem = Convert.ToInt32(Console.ReadLine().Trim());
order.Add(orderItem);
}
int k = Convert.ToInt32(Console.ReadLine().Trim());
var orderedList = order.OrderBy(a=>a).ToList();
int result = Result.filledOrders(orderedList, k);
Console.WriteLine(result);
}
}
I think, the better way to approach (to decrease time complexity) is to solve without use of sorting. (Ofcourse, that comes that cost of readability)
Below is a solution without use of sort. (Not sure if I covered all edge cases.)
import os, sys
def max_fulfilled_orders(order_arr, k):
# track the max no.of orders in the arr.
max_num = 0
# order count, can be fulfilled.
order_count = 0
# iter over order array
for i in range(0, len(order_arr)):
# if remain value < 0 then
if k - order_arr[i] < 0:
# add the max no.of orders to total
k += max_num
if order_count > 0:
# decrease order_count
order_count -= 1
# if the remain value >= 0
if(k - order_arr[i] >= 0):
# subtract the current no.of orders from total.
k -= order_arr[i]
# increase the order count.
order_count += 1
# track the max no.of orders till the point.
if order_arr[i] > max_num:
max_num = order_arr[i]
return order_count
print(max_fulfilled_orders([3, 2, 1], 0)) # Out: 0
print(max_fulfilled_orders([3, 2, 1], 1)) # Out: 1
print(max_fulfilled_orders([3, 1, 1], 2)) # Out: 2
print(max_fulfilled_orders([3, 2, 4], 9)) # Out: 3
print(max_fulfilled_orders([3, 2, 1, 4], 10)) # Out: 4
In python,
def order_fillers(order,k):
if len(order)==0 or k==0:
return 0
order.sort()
max_orders=0
for item in order:
if k<=0:
return max_orders
if item<=k:
max_orders+=1
k-=item
return max_orders
JavaScript Solution
function filledOrders(order, k) {
let total = 0;
let count = 0;
const ordersLength = order.length;
const sortedOrders = order.sort();
for (let i = 0; i < ordersLength; i++) {
if (total + sortedOrders[i] <= k) {
// if all orders able to be filled
if (total <= k && i === ordersLength - 1) return ordersLength;
total += sortedOrders[i];
count++;
} else {
return count;
}
}
}
// Validation
console.log(filledOrders([3, 2, 1], 3)); // 2
console.log(filledOrders([3, 2, 1], 1)); // 1
console.log(filledOrders([3, 2, 1], 10)); // 3
console.log(filledOrders([3, 2, 1], 0)); // 0
console.log(filledOrders([3, 2, 2], 1)); // 0
Given an array of positive integers. How to find a subsequence of length L with max sum which has the distance between any two of its neighboring elements that do not exceed K
I have the following solution but don't know how to take into account length L.
1 <= N <= 100000, 1 <= L <= 200, 1 <= K <= N
f[i] contains max sum of the subsequence that ends in i.
for i in range(K, N)
f[i] = INT_MIN
for j in range(1, K+1)
f[i] = max(f[i], f[i-j] + a[i])
return max(f)
(edit: slightly simplified non-recursive solution)
You can do it like this, just for each iteration consider if the item should be included or excluded.
def f(maxK,K, N, L, S):
if L == 0 or not N or K == 0:
return S
#either element is included
included = f(maxK,maxK, N[1:], L-1, S + N[0] )
#or excluded
excluded = f(maxK,K-1, N[1:], L, S )
return max(included, excluded)
assert f(2,2,[10,1,1,1,1,10],3,0) == 12
assert f(3,3,[8, 3, 7, 6, 2, 1, 9, 2, 5, 4],4,0) == 30
If N is very long you can consider changing to a table version, you could also change the input to tuples and use memoization.
Since OP later included the information that N can be 100 000, we can't really use recursive solutions like this. So here is a solution that runs in O(nKL), with same memory requirement:
import numpy as np
def f(n,K,L):
t = np.zeros((len(n),L+1))
for l in range(1,L+1):
for i in range(len(n)):
t[i,l] = n[i] + max( (t[i-k,l-1] for k in range(1,K+1) if i-k >= 0), default = 0 )
return np.max(t)
assert f([10,1,1,1,1,10],2,3) == 12
assert f([8, 3, 7, 6, 2, 1, 9],3,4) == 30
Explanation of the non recursive solution. Each cell in the table t[ i, l ] expresses the value of max subsequence with exactly l elements that use the element in position i and only elements in position i or lower where elements have at most K distance between each other.
subsequences of length n (those in t[i,1] have to have only one element, n[i] )
Longer subsequences have the n[i] + a subsequence of l-1 elements that starts at most k rows earlier, we pick the one with the maximal value. By iterating this way, we ensure that this value is already calculated.
Further improvements in memory is possible by considering that you only look at most K steps back.
Here is a bottom up (ie no recursion) dynamic solution in Python. It takes memory O(l * n) and time O(l * n * k).
def max_subseq_sum(k, l, values):
# table[i][j] will be the highest value from a sequence of length j
# ending at position i
table = []
for i in range(len(values)):
# We have no sum from 0, and i from len 1.
table.append([0, values[i]])
# By length of previous subsequence
for subseq_len in range(1, l):
# We look back up to k for the best.
prev_val = None
for last_i in range(i-k, i):
# We don't look back if the sequence was not that long.
if subseq_len <= last_i+1:
# Is this better?
this_val = table[last_i][subseq_len]
if prev_val is None or prev_val < this_val:
prev_val = this_val
# Do we have a best to offer?
if prev_val is not None:
table[i].append(prev_val + values[i])
# Now we look for the best entry of length l.
best_val = None
for row in table:
# If the row has entries for 0...l will have len > l.
if l < len(row):
if best_val is None or best_val < row[l]:
best_val = row[l]
return best_val
print(max_subseq_sum(2, 3, [10, 1, 1, 1, 1, 10]))
print(max_subseq_sum(3, 4, [8, 3, 7, 6, 2, 1, 9, 2, 5, 4]))
If I wanted to be slightly clever I could make this memory O(n) pretty easily by calculating one layer at a time, throwing away the previous one. It takes a lot of cleverness to reduce running time to O(l*n*log(k)) but that is doable. (Use a priority queue for your best value in the last k. It is O(log(k)) to update it for each element but naturally grows. Every k values you throw it away and rebuild it for a O(k) cost incurred O(n/k) times for a total O(n) rebuild cost.)
And here is the clever version. Memory O(n). Time O(n*l*log(k)) worst case, and average case is O(n*l). You hit the worst case when it is sorted in ascending order.
import heapq
def max_subseq_sum(k, l, values):
count = 0
prev_best = [0 for _ in values]
# i represents how many in prev subsequences
# It ranges from 0..(l-1).
for i in range(l):
# We are building subsequences of length i+1.
# We will have no way to find one that ends
# before the i'th element at position i-1
best = [None for _ in range(i)]
# Our heap will be (-sum, index). It is a min_heap so the
# minimum element has the largest sum. We track the index
# so that we know when it is in the last k.
min_heap = [(-prev_best[i-1], i-1)]
for j in range(i, len(values)):
# Remove best elements that are more than k back.
while min_heap[0][-1] < j-k:
heapq.heappop(min_heap)
# We append this value + (best prev sum) using -(-..) = +.
best.append(values[j] - min_heap[0][0])
heapq.heappush(min_heap, (-prev_best[j], j))
# And now keep min_heap from growing too big.
if 2*k < len(min_heap):
# Filter out elements too far back.
min_heap = [_ for _ in min_heap if j - k < _[1]]
# And make into a heap again.
heapq.heapify(min_heap)
# And now finish this layer.
prev_best = best
return max(prev_best)
Extending the code for itertools.combinations shown at the docs, I built a version that includes an argument for the maximum index distance (K) between two values. It only needed an additional and indices[i] - indices[i-1] < K check in the iteration:
def combinations_with_max_dist(iterable, r, K):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123
pool = tuple(iterable)
n = len(pool)
if r > n:
return
indices = list(range(r))
yield tuple(pool[i] for i in indices)
while True:
for i in reversed(range(r)):
if indices[i] != i + n - r and indices[i] - indices[i-1] < K:
break
else:
return
indices[i] += 1
for j in range(i+1, r):
indices[j] = indices[j-1] + 1
yield tuple(pool[i] for i in indices)
Using this you can bruteforce over all combinations with regards to K, and then find the one that has the maximum value sum:
def find_subseq(a, L, K):
return max((sum(values), values) for values in combinations_with_max_dist(a, L, K))
Results:
print(*find_subseq([10, 1, 1, 1, 1, 10], L=3, K=2))
# 12 (10, 1, 1)
print(*find_subseq([8, 3, 7, 6, 2, 1, 9, 2, 5, 4], L=4, K=3))
# 30 (8, 7, 6, 9)
Not sure about the performance if your value lists become very long though...
Algorithm
Basic idea:
Iteration on input array, choose each index as the first taken element.
Then Recursion on each first taken element, mark the index as firstIdx.
The next possible index would be in range [firstIdx + 1, firstIdx + K], both inclusive.
Loop on the range to call each index recursively, with L - 1 as the new L.
Optionally, for each pair of (firstIndex, L), cache its max sum, for reuse.
Maybe this is necessary for large input.
Constraints:
array length <= 1 << 17 // 131072
K <= 1 << 6 // 64
L <= 1 << 8 // 256
Complexity:
Time: O(n * L * K)
Since each (firstIdx , L) pair only calculated once, and that contains a iteration of K.
Space: O(n * L)
For cache, and method stack in recursive call.
Tips:
Depth of recursion is related to L, not array length.
The defined constraints are not the actual limit, it could be larger, though I didn't test how large it can be.
Basically:
Both array length and K actually could be of any size as long as there are enough memory, since they are handled via iteration.
L is handled via recursion, thus it does has a limit.
Code - in Java
SubSumLimitedDistance.java:
import java.util.HashMap;
import java.util.Map;
public class SubSumLimitedDistance {
public static final long NOT_ENOUGH_ELE = -1; // sum that indicate not enough element, should be < 0,
public static final int MAX_ARR_LEN = 1 << 17; // max length of input array,
public static final int MAX_K = 1 << 6; // max K, should not be too long, otherwise slow,
public static final int MAX_L = 1 << 8; // max L, should not be too long, otherwise stackoverflow,
/**
* Find max sum of sum array.
*
* #param arr
* #param K
* #param L
* #return max sum,
*/
public static long find(int[] arr, int K, int L) {
if (K < 1 || K > MAX_K)
throw new IllegalArgumentException("K should be between [1, " + MAX_K + "], but get: " + K);
if (L < 0 || L > MAX_L)
throw new IllegalArgumentException("L should be between [0, " + MAX_L + "], but get: " + L);
if (arr.length > MAX_ARR_LEN)
throw new IllegalArgumentException("input array length should <= " + MAX_ARR_LEN + ", but get: " + arr.length);
Map<Integer, Map<Integer, Long>> cache = new HashMap<>(); // cache,
long maxSum = NOT_ENOUGH_ELE;
for (int i = 0; i < arr.length; i++) {
long sum = findTakeFirst(arr, K, L, i, cache);
if (sum == NOT_ENOUGH_ELE) break; // not enough elements,
if (sum > maxSum) maxSum = sum; // larger found,
}
return maxSum;
}
/**
* Find max sum of sum array, with index of first taken element specified,
*
* #param arr
* #param K
* #param L
* #param firstIdx index of first taken element,
* #param cache
* #return max sum,
*/
private static long findTakeFirst(int[] arr, int K, int L, int firstIdx, Map<Integer, Map<Integer, Long>> cache) {
// System.out.printf("findTakeFirst(): K = %d, L = %d, firstIdx = %d\n", K, L, firstIdx);
if (L == 0) return 0; // done,
if (firstIdx + L > arr.length) return NOT_ENOUGH_ELE; // not enough elements,
// check cache,
Map<Integer, Long> map = cache.get(firstIdx);
Long cachedResult;
if (map != null && (cachedResult = map.get(L)) != null) {
// System.out.printf("hit cache, cached result = %d\n", cachedResult);
return cachedResult;
}
// cache not exists, calculate,
long maxRemainSum = NOT_ENOUGH_ELE;
for (int i = firstIdx + 1; i <= firstIdx + K; i++) {
long remainSum = findTakeFirst(arr, K, L - 1, i, cache);
if (remainSum == NOT_ENOUGH_ELE) break; // not enough elements,
if (remainSum > maxRemainSum) maxRemainSum = remainSum;
}
if ((map = cache.get(firstIdx)) == null) cache.put(firstIdx, map = new HashMap<>());
if (maxRemainSum == NOT_ENOUGH_ELE) { // not enough elements,
map.put(L, NOT_ENOUGH_ELE); // cache - as not enough elements,
return NOT_ENOUGH_ELE;
}
long maxSum = arr[firstIdx] + maxRemainSum; // max sum,
map.put(L, maxSum); // cache - max sum,
return maxSum;
}
}
SubSumLimitedDistanceTest.java:
(test case, via TestNG)
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.concurrent.ThreadLocalRandom;
public class SubSumLimitedDistanceTest {
private int[] arr;
private int K;
private int L;
private int maxSum;
private int[] arr2;
private int K2;
private int L2;
private int maxSum2;
private int[] arrMax;
private int KMax;
private int KMaxLargest;
private int LMax;
private int LMaxLargest;
#BeforeClass
private void setUp() {
// init - arr,
arr = new int[]{10, 1, 1, 1, 1, 10};
K = 2;
L = 3;
maxSum = 12;
// init - arr2,
arr2 = new int[]{8, 3, 7, 6, 2, 1, 9, 2, 5, 4};
K2 = 3;
L2 = 4;
maxSum2 = 30;
// init - arrMax,
arrMax = new int[SubSumLimitedDistance.MAX_ARR_LEN];
ThreadLocalRandom rd = ThreadLocalRandom.current();
long maxLongEle = Long.MAX_VALUE / SubSumLimitedDistance.MAX_ARR_LEN;
int maxEle = maxLongEle > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxLongEle;
for (int i = 0; i < arrMax.length; i++) {
arrMax[i] = rd.nextInt(maxEle);
}
KMax = 5;
LMax = 10;
KMaxLargest = SubSumLimitedDistance.MAX_K;
LMaxLargest = SubSumLimitedDistance.MAX_L;
}
#Test
public void test() {
Assert.assertEquals(SubSumLimitedDistance.find(arr, K, L), maxSum);
Assert.assertEquals(SubSumLimitedDistance.find(arr2, K2, L2), maxSum2);
}
#Test(timeOut = 6000)
public void test_veryLargeArray() {
run_printDuring(arrMax, KMax, LMax);
}
#Test(timeOut = 60000) // takes seconds,
public void test_veryLargeArrayL() {
run_printDuring(arrMax, KMax, LMaxLargest);
}
#Test(timeOut = 60000) // takes seconds,
public void test_veryLargeArrayK() {
run_printDuring(arrMax, KMaxLargest, LMax);
}
// run find once, and print during,
private void run_printDuring(int[] arr, int K, int L) {
long startTime = System.currentTimeMillis();
long sum = SubSumLimitedDistance.find(arr, K, L);
long during = System.currentTimeMillis() - startTime; // during in milliseconds,
System.out.printf("arr length = %5d, K = %3d, L = %4d, max sum = %15d, running time = %.3f seconds\n", arr.length, K, L, sum, during / 1000.0);
}
#Test
public void test_corner_notEnoughEle() {
Assert.assertEquals(SubSumLimitedDistance.find(new int[]{1}, 2, 3), SubSumLimitedDistance.NOT_ENOUGH_ELE); // not enough element,
Assert.assertEquals(SubSumLimitedDistance.find(new int[]{0}, 1, 3), SubSumLimitedDistance.NOT_ENOUGH_ELE); // not enough element,
}
#Test
public void test_corner_ZeroL() {
Assert.assertEquals(SubSumLimitedDistance.find(new int[]{1, 2, 3}, 2, 0), 0); // L = 0,
Assert.assertEquals(SubSumLimitedDistance.find(new int[]{0}, 1, 0), 0); // L = 0,
}
#Test(expectedExceptions = IllegalArgumentException.class)
public void test_invalid_K() {
// SubSumLimitedDistance.find(new int[]{1, 2, 3}, 0, 2); // K = 0,
// SubSumLimitedDistance.find(new int[]{1, 2, 3}, -1, 2); // K = -1,
SubSumLimitedDistance.find(new int[]{1, 2, 3}, SubSumLimitedDistance.MAX_K + 1, 2); // K = SubSumLimitedDistance.MAX_K+1,
}
#Test(expectedExceptions = IllegalArgumentException.class)
public void test_invalid_L() {
// SubSumLimitedDistance.find(new int[]{1, 2, 3}, 2, -1); // L = -1,
SubSumLimitedDistance.find(new int[]{1, 2, 3}, 2, SubSumLimitedDistance.MAX_L + 1); // L = SubSumLimitedDistance.MAX_L+1,
}
#Test(expectedExceptions = IllegalArgumentException.class)
public void test_invalid_tooLong() {
SubSumLimitedDistance.find(new int[SubSumLimitedDistance.MAX_ARR_LEN + 1], 2, 3); // input array too long,
}
}
Output of test case for large input:
arr length = 131072, K = 5, L = 10, max sum = 20779205738, running time = 0.303 seconds
arr length = 131072, K = 64, L = 10, max sum = 21393422854, running time = 1.917 seconds
arr length = 131072, K = 5, L = 256, max sum = 461698553839, running time = 9.474 seconds
I need to convert a list of ints to a string containing all the ranges in the list.
So for example, the output should be as follows:
getIntRangesFromList([1,3,7,2,11,8,9,11,12,15]) -> "1-3,7-9,11-12,15"
So the input is not sorted and there can be duplicate values. The lists range in size from one element to 4k elements. The minimum and maximum values are 1 and 4094.
This is part of a performance critical piece of code. I have been trying to optimize this, but I can't find a way to get this faster. This is my current code:
def _getIntRangesFromList(list):
if (list==[]):
return ''
list.sort()
ranges = [[list[0],list[0]]] # ranges contains the start and end values of each range found
for val in list:
r = ranges[-1]
if val==r[1]+1:
r[1] = val
elif val>r[1]+1:
ranges.append([val,val])
return ",".join(["-".join([str(y) for y in x]) if x[0]!=x[1] else str(x[0]) for x in ranges])
Any idea on how to get this faster?
This could be a task for the itertools module.
import itertools
list_num = [1, 2, 3, 7, 8, 9, 11, 12, 15]
groups = (list(x) for _, x in
itertools.groupby(list_num, lambda x, c=itertools.count(): x - next(c)))
print(', '.join('-'.join(map(str, (item[0], item[-1])[:len(item)])) for item in groups))
This will give you 1-3, 7-9, 11-12, 15.
To understand what's going on you might want to check the content of groups.
import itertools
list_num = [1, 2, 3, 7, 8, 9, 11, 12, 15]
groups = (list(x) for _, x in
itertools.groupby(list_num, lambda x, c=itertools.count(): x - next(c)))
for element in groups:
print('element={}'.format(element))
This will give you the following output.
element=[1, 2, 3]
element=[7, 8, 9]
element=[11, 12]
element=[15]
The basic idea is to have a counter running parallel to the numbers. groupby will create individual groups for numbers with the same numerical distance to the current value of the counter.
I don't know whether this is faster on your version of Python. You'll have to check this yourself. In my setting it's slower with this data set, but faster with a bigger number of elements.
The fastest one I could come up, which tests about 10% faster than your solution on my machine (according to timeit):
def _ranges(l):
if l:
l.sort()
return ''.join([(str(l[i]) + ('-' if l[i] + 1 == l[i + 1] else ','))
for i in range(0, len(l) - 1) if l[i - 1] + 2 != l[i + 1]] +
[str(l[-1])])
else: return ''
The above code assumes that the values in the list are unique. If they aren't, it's easy to fix but there's a subtle hack which will no longer work and the end result will be slightly slower.
I actually timed _ranges(u[:]) because of the sort; u is 600 randomly selected integers from range(1000) comprising 235 subsequences; 83 are singletons and 152 contain at least two numbers. If the list is sorted, quite a lot of time is saved.
def _to_range(l, start, stop, idx, result):
if idx == len(l):
result.append((start, stop))
return result
if l[idx] - stop > 1:
result.append((start, stop))
return _to_range(l, l[idx], l[idx], idx + 1, result)
return _to_range(l, start, l[idx], idx + 1, result)
def get_range(l):
if not l:
return []
return _to_range(l, start = l[0], stop = l[0], idx = 0, result = [])
l = [1, 2, 3, 7, 8, 9, 11, 12, 15]
result = get_range(l)
print(result)
>>> [(1, 3), (7, 9), (11, 12), (15, 15)]
# I think it's better to fetch the data as it is and if needed, change it
# with
print(','.join('-'.join([str(start), str(stop)]) for start, stop in result))
>>> 1-3,7-9,11-12,15-15
Unless you don't care at all about the data, then u can just append str(start) + '-' + str(stop) in _to_range function so later there will be no need to type extra '-'.join method.
I'll concentrate on the performance that is your main issue. I'll give 2 solutions:
1) If the boundaries of the integers stored is between A and B, and you can create an array of booleans(even you can choose an array of bits for expanding the range you can storage) with (B - A + 2) elements, e.g. A = 0 and B = 1 000 000, we can do this (i'll write it in C#, sorry XD). This run in O(A - B) and is a good solution if A - B is less than the number of numbers:
public string getIntRangesFromList(int[] numbers)
{
//You can change this 2 constants
const int A = 0;
const int B = 1000000;
//Create an array with all its values in false by default
//Last value always will be in false in propourse, as you can see it storage 1 value more than needed for 2nd cycle
bool[] apparitions = new bool[B - A + 2];
int minNumber = B + 1;
int maxNumber = A - 1;
int pos;
for (int i = 0; i < numbers.Length; i++)
{
pos = numbers[i] - A;
apparitions[pos] = true;
if (minNumber > pos)
{
minNumber = pos;
}
if (maxNumber < pos)
{
maxNumber = pos;
}
}
//I will mantain the concatenation simple, but you can make it faster to improve performance
string result = "";
bool isInRange = false;
bool isFirstRange = true;
int firstPosOfRange = 0; //Irrelevant what is its initial value
for (int i = minNumber; i <= maxNumber + 1; i++)
{
if (!isInRange)
{
if (apparitions[i])
{
if (!isFirstRange)
{
result += ",";
}
else
{
isFirstRange = false;
}
result += (i + A);
isInRange = true;
firstPosOfRange = i;
}
}
else
{
if (!apparitions[i])
{
if (i > firstPosOfRange + 1)
{
result += "-" + (i + A - 1);
}
isInRange = false;
}
}
}
return result;
}
2) O(N * log N)
public string getIntRangesFromList2(int[] numbers)
{
string result = "";
if (numbers.Length > 0)
{
numbers.OrderBy(x => x); //sorting and making the algorithm complexity O(N * log N)
result += numbers[0];
int countNumbersInRange = 1;
for (int i = 1; i < numbers.Length; i++)
{
if (numbers[i] != numbers[i - 1] + 1)
{
if (countNumbersInRange > 1)
{
result += "-" + numbers[i - 1];
}
result += "," + numbers[i];
countNumbersInRange = 1;
}
else
{
countNumbersInRange++;
}
}
}
return result;
}
I have been given a set S, of n integers, and have to print the size of a maximal subset S' of S where the sum of any 2 numbers in S' are not evenly divisible by k.
Input Format
The first line contains 2 space-separated integers, n and k, respectively.
The second line contains n space-separated integers describing the unique values of the set.
My Code :
import sys
n,k = raw_input().strip().split(' ')
n,k = [int(n),int(k)]
a = map(int,raw_input().strip().split(' '))
count = 0
for i in range(len(a)):
for j in range(len(a)):
if (a[i]+a[j])%k != 0:
count = count+1
print count
Input:
4 3
1 7 2 4
Expected Output:
3
My Output:
10
What am i doing wrong? Anyone?
You can solve it in O(n) time using the following approach:
L = [0]*k
for x in a:
L[x % k] += 1
res = 0
for i in range(k//2+1):
if i == 0 or k == i*2:
res += bool(L[i])
else:
res += max(L[i], L[k-i])
print(res)
Yes O(n) solution for this problem is very much possible. Like planetp rightly pointed out its pretty much the same solution I have coded in java. Added comments for better understanding.
import java.io.; import java.util.;
public class Solution {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n=in.nextInt();
int k=in.nextInt();
int [] arr = new int[k];
Arrays.fill(arr, 0);
Map<Integer,Integer> mp=new HashMap<>();
Storing the values in a map considering there are no duplicates. You can store them in array list if there are duplicates. Only then you have different results.
for(int i=0;i
int res=0;
for(int i=0;i<=(k/2);i++)
{
if(i==0 || k==i*2)
{
if(arr[i]!=0)
res+=1;
}
If the no. is divisible by k we can have only one and if the no is exactly half of k then we can have only 1. Rational if a & b are divisble by k then a+b is also divisible by k. Similarly if c%k=k/2 then if we have more than one such no. their combination is divisible by k. Hence we restrict them to 1 value each.
else
{
int p=arr[i];
int q=arr[k-i];
if(p>=q)
res+=p;
else
res+=q;
}
This is simple figure out which is more from a list of 0 to k/2 in the list if a[x]>a[k-x] get the values which is greater. i.e. if we have k=4 and we have no. 1,3,5,7,9,13,17. Then a[1]=4 and a[3]=2 thus pick a[1] because 1,5,13,17 can be kept together.
}
System.out.println(res);
}
}
# given k, n and a as per your input.
# Will return 0 directly if n == 1
def maxsize(k, n, a):
import itertools
while n > 1:
sets = itertools.combinations(a, n)
for set_ in sets:
if all((u+v) % k for (u, v) in itertools.combinations(set_, 2)):
return n
n -= 1
return 0
Java solution
public class Solution {
static PrintStream out = System.out;
public static void main(String[] args) {
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
Scanner in = new Scanner (System.in);
int n = in.nextInt();
int k = in.nextInt();
int[] A = new int[n];
for(int i=0;i<n;i++){
A[i]=in.nextInt();
}
int[] R = new int[k];
for(int i=0;i<n;i++)
R[A[i] % k]+=1;
int res=0;
for(int i=0;i<k/2+1;i++){
if(i==0 || k==i*2)
res+= (R[i]!=0)?1:0;
else
res+= Math.max(R[i], R[k-i]);
}
out.println(res);
}
}