I have this Java problem, which I suspect it relates to a higher-level algorithm, but my searches haven't been able to come up with anything practical.
You construct an array as follows:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
Basically, Ai,j = Ai-1,j-1+Ai-1,j. It's supposed to return the element at index (l, c): for (4, 1) it should return 4, (5, 2) returns 10, etc. My solution is straightforward, but it is not enough:
static long get(int l, int c){
long[][] matrix = new long[l+1][l+1];
matrix[0][0]=1;
matrix[1][0]=1;
matrix[1][1]=1;
for(int i=2;i<=l;i++){
matrix[i][0]=1;
for(int j=1;j<=i;j++){
matrix[i][j] = matrix[i-1][j-1]+matrix[i-1][j];
}
}
return matrix[l][c];
}
It doesn't work for large values of l and c. Using BigInteger doesn't work. My searches have led me to loop skewing and scalarization, but I don't know where to start. Any steer in the right direction is really appreciated.
PS: Sorry for the newbie-vibe, this is my first question!
You are describing Pascal's triangle, for which a closed formula exists:
matrix[n][k] = n!/(k! * (n-k)!)
P.S. If these numbers seem familiar, it is because they are also from the binomial theorem, where the common examples are:
(x+y)^2 = 1* x^2 + 2xy + 1*y^2
(x+y)^3 = 1*x^3 + 3*xy^2 + 3yx^2 + 1*y^3
You don't need to use a loop, this is simply pascal's triangle, the formula:
(n, k) = n! / ( k! * (n-k)!)
Will generate your answer for the position (n, k).
Try this:
static long get(int l, int c) throws Exception {
if (l != c)
throw new Exception("l != c");
long[][] matrix = new long[l+1][l+1];
matrix[0][0]=1;
for (int i = 1; i <= l; ++i) {
for (int j = 0; j <= i; ++j) {
if (j - 1 >= 0) {
matrix[i][j] = matrix[i - 1][j] + matrix[i - 1][j - 1];
} else {
matrix[i][j] = matrix[i - 1][j];
}
}
}
return matrix[l][c];
}
Related
This is the question:
You are given Q queries. Each query consists of a single number N . You can perform any of the operations on in each move:
If we take 2 integers a and b where N=a*b (a ,b cannot be equal to 1), then we can change N=max(a,b)
Decrease the value of N by 1 .
Determine the minimum number of moves required to reduce the value of to .
Input Format
The first line contains the integer Q.
The next Q lines each contain an integer,N .
Output Format
Output Q lines. Each line containing the minimum number of moves required > to reduce the value of N to 0.
I have written the following code. This code is giving some wrong answers and also giving time limit exceed error . Can you tell what are the the mistakes present in my code ? where or what I am doing wrong here?
My code:
public static int downToZero(int n) {
// Write your code here
int count1=0;
int prev_i=0;
int prev_j=0;
int next1=0;
int next2=Integer.MAX_VALUE;
if (n==0){
return 0;
}
while(n!=0){
if(n==1){
count1++;
break;
}
next1=n-1;
outerloop:
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
if (i*j==n){
if (prev_i ==j && prev_j==i){
break outerloop;
}
if (i !=j){
prev_i=i;
prev_j=j;
}
int max=Math.max(i,j);
if (max<next2){
next2=max;
}
}
}
}
n=Math.min(next1,next2);
count1++;
}
return count1;
}
This is part is coded for us:
public class Solution {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
int q = Integer.parseInt(bufferedReader.readLine().trim());
for (int qItr = 0; qItr < q; qItr++) {
int n = Integer.parseInt(bufferedReader.readLine().trim());
int result = Result.downToZero(n);
bufferedWriter.write(String.valueOf(result));
bufferedWriter.newLine();
}
bufferedReader.close();
bufferedWriter.close();
}
}
Ex: it is not working for number 7176 ....
To explore all solution tree and find globally optimal solution, we must choose the best result both from all possible divisor pairs and from solution(n-1)
My weird translation to Java (ideone) uses bottom-up dynamic programming to make execution faster.
We calculate solutions for values i from 1 to n, they are written into table[i].
At first we set result into 1 + best result for previous value (table[i-1]).
Then we factor N into all pairs of divisors and check whether using already calculated result for larger divisor table[d] gives better result.
Finally we write result into the table.
Note that we can calculate table once and use it for all Q queries.
class Ideone
{
public static int makezeroDP(int n){
int[] table = new int[n+1];
table[1] = 1; table[2] = 2; table[3] = 3;
int res;
for (int i = 4; i <= n; i++) {
res = 1 + table[i-1];
int a = 2;
while (a * a <= i) {
if (i % a == 0)
res = Math.min(res, 1 + table[i / a]);
a += 1;
}
table[i] = res;
}
return table[n];
}
public static void main (String[] args) throws java.lang.Exception
{
int n = 145;//999999;
System.out.println(makezeroDP(n));
}
}
Old part
Simple implementation (sorry, in Python) gives answer 7 for 7176
def makezero(n):
if n <= 3:
return n
result = 1 + makezero(n - 1)
t = 2
while t * t <= n:
if n % t == 0:
result = min(result, 1 + makezero(n // t))
t += 1
return result
In Python it's needed to set recursion limit or change algorithm. Now use memoization, as I wrote in comments).
t = [-i for i in range(1000001)]
def makezeroMemo(n):
if t[n] > 0:
return t[n]
if t[n-1] < 0:
res = 1 + makezeroMemo(n-1)
else:
res = 1 + t[n-1]
a = 2
while a * a <= n:
if n % a == 0:
res = min(res, 1 + makezeroMemo(n // a))
a += 1
t[n] = res
return res
Bottom-up table dynamic programming. No recursion.
def makezeroDP(n):
table = [0,1,2,3] + [0]*(n-3)
for i in range(4, n+1):
res = 1 + table[i-1]
a = 2
while a * a <= i:
if i % a == 0:
res = min(res, 1 + table[i // a])
a += 1
table[i] = res
return table[n]
We can construct the directed acyclic graph quickly with a sieve and
then compute shortest paths. No trial division needed.
Time and space usage is Θ(N log N).
n_max = 1000000
successors = [[n - 1] for n in range(n_max + 1)]
for a in range(2, n_max + 1):
for b in range(a, n_max // a + 1):
successors[a * b].append(b)
table = [0]
for n in range(1, n_max + 1):
table.append(min(table[s] for s in successors[n]) + 1)
print(table[7176])
Results:
7
EDIT:
The algorithm uses Greedy approach and doesn't return optimal results, it just simplifies OP's approach. For 7176 given as example, below algorithm returns 10, I can see a shorter chain of 7176 -> 104 -> 52 -> 13 -> 12 -> 4 -> 2 -> 1 -> 0 with 8 steps, and expected answer is 7.
Let's review your problem in simple terms.
If we take 2 integers a and b where N=a*b (a ,b cannot be equal to 1), then we can change N=max(a,b)
and
Determine the minimum number of moves required to reduce the value of to .
You're looking for 2 factors of N, a and b and, if you want the minimum number of moves, this means that your maximum at each step should be minimum. We know for a fact that this minimum is reached when factors are closest to N. Let me give you an example:
36 = 1 * 36 = 2 * 18 = 3 * 12 = 4 * 9 = 6 * 6
We know that sqrt(36) = 6 and you can see that the minimum of 2 factors you can get at this step is max(6, 6) = 6. Sure, 36 is 6 squared, let me take a number without special properties, 96, with its square root rounded down to nearest integer 9.
96 = 2 * 48 = 3 * 32 = 4 * 24 = 6 * 16 = 8 * 12
You can see that your minimum value for max(a, b) is max(8, 12) = 12, which is, again, attained when factors are closest to square root.
Now let's look at the code:
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
if (i*j==n){
You can do this in one loop, knowing that n / i returns an integer, therefore you need to check if i * (n / i) == n. With the previous observation, we need to start at the square root, and go down, until we get to 1. If we got i and n / i as factors, we know that this pair is also the minimum you can get at this step. If no factors are found and you reach 1, which obviously is a factor of n, you have a prime number and you need to use the second instruction:
Decrease the value of N by 1 .
Note that if you go from sqrt(n) down to 1, looking for factors, if you find one, max(i, n / i) will be n / i.
Additionally, if n = 1, you take 1 step. If n = 2, you take 2 steps (2 -> 1). If n = 3, you take 3 steps (3 -> 2 -> 1). Therefore if n is 1, 2 or 3, you take n steps to go to 0. OK, less talking, more coding:
static int downToZero(int n) {
if (n == 1 || n == 2 || n == 3) return n;
int sqrt = (int) Math.sqrt(n);
for (int i = sqrt; i > 1; i--) {
if (n / i * i == n) {
return 1 + downToZero(n / i);
}
}
return 1 + downToZero(n - 1);
}
Notice that I'm stopping when i equals 2, I know that if I reach 1, it's a prime number and I need to go a step forward and look at n - 1.
However, I have tried to see the steps your algorithm and mine takes, so I've added a print statement each time n changes, and we both have the same succession: 7176, 92, 23, 22, 11, 10, 5, 4, 2, 1, which returns 10. Isn't that correct?
So, I found a solution which is working for all the test cases -
static final int LIMIT = 1_000_000;
static int[] solutions = buildSolutions();
public static int downToZero(int n) {
// Write your code here
return solutions[n];
}
static int[] buildSolutions() {
int[] solutions = new int[LIMIT + 1];
for (int i = 1; i < solutions.length; i++) {
solutions[i] = solutions[i - 1] + 1;
for (int j = 2; j * j <= i; j++) {
if (i % j == 0) {
solutions[i] = Math.min(solutions[i], solutions[i / j] + 1);
}
}
}
return solutions;
}
}
Given a two 1d arrays that are stick one to the other with n and m lengths , write a recursive algorithm to find the number of ways that this shape could be filled by 1x1 or 1x2 or 2x1 blocks ,
here is my attempt , but I believe that I'm counting the same option several times :
public static int foo(int n1 ,int m){
if(n1==0 && m ==0){
return 1;
}
if(n1 < 0 || m < 0)
return 0;
return (foo(n1-1,m)+foo(n1,m-1)+foo(n1-1,m-1) +foo(n1,m-2) + foo(n1-2,m));
}
*** UPDATE ****
now the code compiles.
Examples :
input foo(2,2) output : 21 , the right answer is 7 .
input foo(4,3) output : 417, the right answer is 32.
these are the options for foo(2,2).
We'll assume n < m. If this is not the case we can just reverse the arguments - this makes the code simpler.
Once we've dealt with the terminating conditions we use a decrease-and-conquer strategy to reduce the input according to the following rules: if n == m, we can reduce both n & m by 1 two ways, n & m by 2 one way, n by 1 and m by 2 one way, and n by 2 and m by 1 one way. If n < m we can reduce m by 1 one way and m by 2 one way.
static int foo(int n, int m)
{
if(n > m) return foo(m, n);
if(n < 0 || m < 0) return 0;
if(n == 0 && m == 0) return 1;
if(n == m) return 2*foo(n-1, m-1) + foo(n-2, m-2) + foo(n-1, m-2) + foo(n-2, m-1);
return foo(n, m-1) + foo(n, m-2);
}
Test:
for(int i=0; i<5; i++)
for(int j=i; j<5; j++)
System.out.format("(%d, %d) = %d%n", i, j, foo(i, j));
Output:
(0, 0) = 1
(0, 1) = 1
(0, 2) = 2
(0, 3) = 3
(0, 4) = 5
(1, 1) = 2
(1, 2) = 3
(1, 3) = 5
(1, 4) = 8
(2, 2) = 7
(2, 3) = 10
(2, 4) = 17
(3, 3) = 22
(3, 4) = 32
(4, 4) = 71
For the case n == m (2, 7, 22, 71, ...) this is a known integer sequence (A030186).
And just for reference, here are the 32 configurations for (3,4):
I believe that i have found the correct answer to my question :
yet i'm not closing this problem until someone with better knowledge than me confirm my answer
public static int foo(int n1 ,int m){
if(n1==0 && m ==0){
return 1;
}
if(n1 < 0 || m < 0)
return 0;
if(m == n1){
return Integer.max(foo(n1-1,m),foo(n1,m-1)) + Integer.max(foo(n1-2,m),foo(n1,m-2))+ foo(n1-1,m-1);
}else{
return Integer.max(foo(n1-1,m),foo(n1,m-1)) + Integer.max(foo(n1-2,m),foo(n1,m-2));
}
}
now i'm taking only the maximum sub-Problem answer so I won't count the same option more than once.
I am solving the Codility problem provided below,
Let A be a non-empty array consisting of N integers.
The abs sum of two for a pair of indices (P, Q) is the absolute value |A[P] + A[Q]|, for 0 ≤ P ≤ Q < N.
For example, the following array A:
A[0] = 1 A1 = 4 A[2] = -3 has pairs of indices (0, 0), (0,
1), (0, 2), (1, 1), (1, 2), (2, 2). The abs sum of two for the pair
(0, 0) is A[0] + A[0] = |1 + 1| = 2. The abs sum of two for the pair
(0, 1) is A[0] + A1 = |1 + 4| = 5. The abs sum of two for the pair
(0, 2) is A[0] + A[2] = |1 + (−3)| = 2. The abs sum of two for the
pair (1, 1) is A1 + A1 = |4 + 4| = 8. The abs sum of two for the
pair (1, 2) is A1 + A[2] = |4 + (−3)| = 1. The abs sum of two for
the pair (2, 2) is A[2] + A[2] = |(−3) + (−3)| = 6.`
Write a function:
class Solution { public int solution(int[] A); }
that, given a non-empty array A consisting of N integers, returns the minimal abs sum of two for any pair of indices in this array.
For example, given the following array A:
A[0] = 1 A1 = 4 A[2] = -3 the function should return 1, as
explained above.
Given array A:
A[0] = -8 A1 = 4 A[2] = 5 A[3] =-10 A[4] = 3 the function
should return |(−8) + 5| = 3.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [1..100,000]; each element of array A is an integer within the range [−1,000,000,000..1,000,000,000].
I write the solution provided below.
public static int solution(int[] A) {
int N = A.length;
Arrays.sort(A);
if (A[0] >= 0) {
return 2 * A[0];
}
int i = 0;
int j = N - 1;
int sum = Math.abs((A[i] + A[j]));
// -10, -8, 3, 4, 5
while (i <= j) {
if (Math.abs(A[i + 1] + A[j]) < sum) {
++i;
sum = Math.abs(A[i] + A[j]);
} else if (Math.abs(A[i] + A[j - 1]) < sum) {
--j;
sum = Math.abs(A[i] + A[j]);
} else {
i++;
j--;
}
}
return sum;
}
The solution gets hanged in the online judge and seems it enters in a forever loop. Is there a possibility that the code can enter a non-ending loop?
UPDATE
After I update the solution with the all negatives check, the code passed the online judge and provides a good performance.
if(A[N-1] <=0){
return 2* Math.abs(A[N-1]);
}
For input arrays e.g({-1, -2, -3}, {-1, -2}, {-1} your algorithm throws ArrayIndexOutOfBoundsException, so arrays when there are only negative numbers and there is no repeats
There is no chance to reach endless loop because either i or j change only + or - 1
I would like to explain the algorithm that I have implemented and then the implementation in C++.
Sort the array because otherwise we would need to check any two arbitrary indices. That brute-force solution would result in O(N ^ 2) runtime complexity.
We initialise the min abs sum to something higher than the possible value in the arrays.
Apply the caterpillar method by having front and back indices.
In every iteration, update the min abs sum as and when needed.
There two special cases:
a. all values are zero or positive. We could return A[front] * 2 early.
b. all values are negative or zero. We could return A[back] * 2 early.
In both cases, we could return early, however, it would result in a bit more code, so I personally avoid that. In the above cases, we can still go through the array without degrading the overall runtime complexity. In these cases, it also does not matter how we go through the array with regards to the result, so I just go through the array in one way in both cases. But the code will be shared with the third case.
In the third case, where the array, and thereby the sorted array, contains both negative and positive values, we try to keep the sum of front and back to zero since this when the abs sum value is minimised. In other words, we try to keep the balance between the negative and positive numbers by keeping their distance to the minimum.
Therefore, if the sum of front and back are less than zero, then we know that the negative front value is greater than the positive back value. As a direct consequence of that, we need to advance the front index.
If the sum of front and back are equal to zero, then we found the smallest min abs sum that is ever possible. The absolute value cannot be less than zero. Whilst I could return at this point in the code for some constant optimisation, I do not do so to keep the code the minimal and also functional.
If the sum of front and back are greater than zero, then we know that the negative front value is less than the positive back value. As a direct consequence of that, we need to decrease the index.
We loop until the front and back indices meet, but we handle the case when they are equal since according to the task specification, the same index can be used twice for the absolute sum.
The runtime complexity of this solution is O(N * logN) because the sorting of the array is O(N * logN) and the loop is O(N) since we go through every element. Therefore, the sorting runtime complexity dominates the loop.
The space complexity is O(1) because we only allocate constant space independently from the number of inputs. The sorting is done in place.
int solution(vector<int> &A)
{
const int N = A.size();
sort(A.begin(), A.end());
int min_abs_sum_of_two = INT_MAX;
for (int front = 0, back = N - 1; front <= back;) {
const int ef = A[front];
const int eb = A[back];
min_abs_sum_of_two = min(min_abs_sum_of_two, abs(ef + eb));
if (ef + eb < 0) ++front; else --back;
}
return min_abs_sum_of_two;
}
I got 100% for following code ( java). Slightly modified version of https://www.techiedelight.com/find-pair-array-minimum-absolute-sum/
import java.util.*;
// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");
class Solution {
public int solution(int[] A) {
// sort the array if it is unsorted
Arrays.sort(A);
int low = 0;
int high = A.length - 1;
if (A[0] >= 0) {
return 2 * A[0];
}
if (A[high] <= 0) {
return -2 * A[high];
}
// maintain two indexes pointing to endpoints of the array
// `min` stores the minimum absolute difference
int min = Integer.MAX_VALUE;
int i = 0, j = 0;
// reduce the search space `A[low…high]` at each iteration of the loop
int sum = 0;
// loop if `low` is less than `high`
while (low < high)
{
// update the minimum if the current absolute sum is less.
sum = A[high] + A[low];
if (Math.abs(sum) < min)
{
min = Math.abs(sum);
i = low;
j = high;
}
// optimization: pair with zero-sum is found
if (min == 0) {
break;
}
// increment `low` index if the total is less than 0;
// decrement `high` index if the total is more than 0
if (sum < 0) {
low++;
}
else {
high--;
}
}
return min;
}
}
The Answer is late but I hope to help anyone have the same question
//============================================================================
// Author: Hamdy Abd El Fattah
// Code is like humor. When you have to explain it, it’s bad.
//============================================================================
#include <bits/stdc++.h>
#define FIO cin.tie(0), cin.sync_with_stdio(0)
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
int main() {
FIO;
ll n , m = INF , x;
vector<ll> positive,negative;
cin>>n;
while(n--){
cin>>x;
if(x < 0)
negative.push_back(x * -1);
else if(x > 0)
positive.push_back(x);
else
m = 0;
}
if(m == 0){
cout<<"0\n";
}else{
sort(positive.begin(), positive.end());
sort(negative.begin(), negative.end());
int i= 0, j = 0;
int positive_size = positive.size(), negative_size =negative.size();
while(i < positive_size || j < negative_size){
if(abs(positive[i] - negative[j]) < m){
m=abs(positive[i] - negative[j]);
m=min(min(m,positive[i]*2),negative[j] * 2);
}
if((i < positive_size && positive[i] <= negative[j]) || j == negative_size)
i++;
else if((j < negative_size && positive[i] > negative[j]) || i == positive_size)
j++;
}
cout<<m<<endl;
}
return 0;
}
I need to calculate a path from [0,0] to [M, N] with min-sum in a matrix moving only right or down?
I found such link https://www.programcreek.com/2014/05/leetcode-minimum-path-sum-java/ but Dynamic Programming option is not clear at all.
I was trying to implement it on my own with BFS algorithm but it is a slow solution
public int minPathSum(final int[][] grid) {
if (grid.length == 1 && grid[0].length == 1) {
return grid[0][0];
}
final int[][] moves = {new int[]{1, 0}, new int[]{0, 1}};
final Queue<int[]> positions = new ArrayDeque<>();
final Queue<Integer> sums = new ArrayDeque<>();
positions.add(new int[]{0, 0});
sums.add(grid[0][0]);
int minSum = Integer.MAX_VALUE;
while (!positions.isEmpty()) {
final int[] point = positions.poll();
final int sum = sums.poll();
for (final int[] move : moves) {
final int x = point[0] + move[0];
final int y = point[1] + move[1];
if (x == grid.length - 1 && y == grid[0].length - 1) {
minSum = Math.min(minSum, sum);
} else if (x > -1 && y > -1 && x < grid.length && y < grid[0].length) {
positions.add(new int[]{x, y});
sums.add(sum + grid[x][y]);
}
}
}
return minSum + grid[grid.length - 1][grid[0].length - 1];
}
Could you please explain and if possible provide how will you solve it?
I'm a bit confused by how you could implement a breadth first search but have trouble understanding the dynamic formulation here, which to me seems simpler :)
This is pretty much the classic dynamic programming problem. Arriving at any cell, solution[y][x], except the first, has at most two predecessors: option 1 and option 2. Assume that we knew the optimal solution for reaching each of those, which edge would we choose? Clearly the better of the two options!
Slightly more formally, if M holds the given values:
solution[0][0] = M[0][0]
// only one choice along
// the top horizontal and
// left vertical
solution[0][x] =
M[0][x] + solution[0][x - 1]
solution[y][0] =
M[y][0] + solution[y - 1][0]
// two choices otherwise:
// the best of option 1 or 2
solution[y][x] =
M[y][x] + min(
solution[y][x - 1],
solution[y - 1][x]
)
We can see that we can create an appropriate routine, with for loops for example, to visit the cells of our solution matrix in "bottom-up" order since each cell's value depends on one or two predecessors that we would have already calculated.
JavaScript code:
function show(M){
let str = '';
for (let row of M)
str += JSON.stringify(row) + '\n';
console.log(str);
}
function f(M){
console.log('Input:\n');
show(M);
let solution = new Array();
for (let i=0; i<M.length; i++)
solution.push(new Array(M[0].length).fill(Infinity));
solution[0][0] = M[0][0];
// only one choice along
// the top horizontal and
// left vertical
for (let x=1; x<M[0].length; x++)
solution[0][x] =
M[0][x] + solution[0][x - 1];
for (let y=1; y<M.length; y++)
solution[y][0] =
M[y][0] + solution[y - 1][0];
console.log('Solution borders:\n');
show(solution);
// two choices otherwise:
// the best of option 1 or 2
for (let y=1; y<M.length; y++)
for (let x=1; x<M[0].length; x++)
solution[y][x] =
M[y][x] + Math.min(
solution[y][x - 1],
solution[y - 1][x]
);
console.log('Full solution:\n');
show(solution);
return solution[M.length-1][M[0].length-1];
}
let arr = [];
arr[0] = [0, 7, -7];
arr[1] = [6, 7, -8];
arr[2] = [1, 2, 0];
console.log(f(arr));
The path to reach (m, n) must be through one of the 2 cells: (m-1, n) or (n-1, m). So minimum sum to reach (m, n) can be written as “minimum of the 2 cells plus sum[m][n]”.
minSum(m, n) = min (minSum(m-1, n-1), minSum(m-1, n)) + sums[m][n]
Given a suffix array, a TopCoder task from SRM 630 asks to find the minium number of distinct characters in the string that could form a string with the given suffix array. The full problem statement can be found on the TopCoder website.
The best solution I found is right here: https://github.com/ftiasch/acm-icpc/blob/6db1ed02a727611830b974a1d4de38bab8f390f9/topcoder/single-round-match/single-round-match-630/SuffixArrayDiv1.java
Here is the algorithm written by ftiasch:
public int minimalCharacters(int[] array) {
int n = array.length;
int[] position = new int[n + 1];
for (int i = 0; i < n; ++i) {
position[array[i]] = i;
}
position[n] = -1;
int[] minimum = new int[n + 1];
for (int i = n - 1; i >= 0; --i) {
minimum[i] = Integer.MAX_VALUE;
for (int j = i + 1; j <= n; ++j) {
boolean valid = true;
for (int x = i; x < j; ++x) {
for (int y = x + 1; y < j; ++y) {
valid &= position[array[x] + 1] < position[array[y] + 1];
}
}
if (valid && minimum[j] < Integer.MAX_VALUE) {
minimum[i] = Math.min(minimum[i], minimum[j] + 1);
}
}
}
return minimum[0];
}
I understand that this is a dynamic programming algorithm but how does it work? I really need a hand understanding it.
EDIT
Here is what ftiasch wrote me back:
hi Ariel,
First of all, thanks to your compliment. Frankly speaking, my solution
is not the best solution to the problem. The optimal one runs in O(n)
time but mine takes O(n^4). I just picked this idea during the contest
because n is relatively small.
Keep in mind that same characters become continuous in the SA. Since
the problem asked for the least number of characters, so I decided to
use dynamic programming to partition the SA into consecutive segments
so that each segments start with the same character.
Which condition is necessary for S[SA[i]] == S[SA[j]] assumed that i <
j? The lexicographic comparison suggests that suffix(SA[i] + 1) should
be smaller than suffix(SA[j] + 1). We can easily find that the
condition is also sufficient.
Write to me if you have any other question. :)
EDIT1
We finally managed to make it work, thanks to David. Here is the linear time algorithm in java from David's Python version:
public int minimalCharacters(int[] array) {
int n = array.length, i;
if (n == 0)
return 0;
int[] array1 = new int[n + 1];
for (i = 0; i < n; i++)
array1[1 + i] = array[i];
int[] position = new int[n + 1];
for (i = 0; i < n + 1; i++)
position[array1[i]] = i;
int k = 1;
for (i = n; i > 1; i--) {
if (position[array1[i] + 1] <= position[array1[i - 1] + 1])
k++;
}
return k;
}
Here’s a quadratic-time algorithm. The suffix array specifies for each
pair of suffixes how they compare lexicographically (and the empty
suffix always is less than all of them). Let s be the unknown string
and suppose that we’re comparing suffix s[i...] with suffix s[j...].
If s[i] != s[j], then the comparison of s[i] and s[j] settles it.
Otherwise, the result is the same as if we compare s[i+1...] and
s[j+1...].
Suppose that we wish to ensure that s[i...] < s[j...]. Clearly we need
s[i] <= s[j]. In fact, unless s[i+1...] < s[j+1...], we need the
strict inequality s[i] < s[j], as otherwise the tiebreaker will go the
wrong way. Otherwise, s[i] == s[j] will suffice regardless of the rest
of the string. Gather up all of the inequalities as arcs in a directed
graph with vertices corresponding to positions in s. This graph is
necessarily acyclic by the total order on suffixes. Make each arc length
1 if the inequality is strict and length 0 otherwise. Output the length
of the longest path, plus one (or zero if the graph is empty).
At least this many distinct letters are needed, by the corresponding
chain of inequalities. What’s perhaps less clear is that this many
distinct letters suffices, but if we determine the label of each
vertex/position in s by the length of the longest path starting there,
then the head and tail of each arc are labeled appropriately.
To get down to linear time, we can exploit the structure of the
graph. It’s straightforward (though not trivial; the graph is metric
after all) to show that the path visiting all vertices of the graph is
the longest, so we merely have to compute its length.
Below are a transliterated version of the sample code (minChars1), an
implementation straight from the description above (minChars2, now
stripped of all comprehension usage), a brute force solution
(minChars3), and the linear-time solution (minChars4).
import itertools
def minChars1(array):
n = len(array)
position = [-1] * (n + 1)
for i in range(n):
position[array[i]] = i
infinity = n + 1
minimum = [infinity] * (n + 1)
minimum[n] = 0
for i in range(n - 1, -1, -1):
for j in range(i + 1, n + 1):
valid = True
for x in range(i, j):
for y in range(x + 1, j):
valid = valid and position[array[x] + 1] < position[array[y] + 1]
if valid and minimum[j] < infinity:
minimum[i] = min(minimum[i], minimum[j] + 1)
return minimum[0]
def lengthOfLongestPath(graph, memo, i):
if i not in memo:
result = 0
for w, j in graph[i]:
result = max(result, w + lengthOfLongestPath(graph, memo, j))
memo[i] = result
return memo[i]
def minChars2(array):
n = len(array)
position = [-1] * (n + 1)
for i in range(n):
position[array[i]] = i
graph = {}
for i in range(n):
graph[i] = []
for j in range(n):
if position[i] > position[j]:
w = 0 if position[i + 1] > position[j + 1] else 1
graph[i].append((w, j))
memo = {None: -1}
for i in range(n):
lengthOfLongestPath(graph, memo, i)
return max(memo.values()) + 1
def minChars3(array):
n = len(array)
position = [None] * n
for i in range(n):
position[array[i]] = i
for k in range(n):
for s in itertools.product(range(k), repeat=n):
valid = True
for i in range(n):
for j in range(n):
valid = valid and (s[i:] < s[j:]) == (position[i] < position[j])
if valid:
return k
return n
def minChars4(array):
n = len(array)
if n == 0:
return 0
array1 = [n] * (n + 1)
for i in range(n):
array1[1 + i] = array[i]
position = [None] * (n + 1)
for i in range(n + 1):
position[array1[i]] = i
k = 1
for i in range(n, 1, -1):
if position[array1[i] + 1] <= position[array1[i - 1] + 1]:
k += 1
return k
def test():
for n in range(7):
for array in itertools.permutations(range(n)):
assert minChars1(array) == minChars2(array) == minChars3(array) == minChars4(array)
test()