Dynamic Programming algorithm for Longest Common Subsequence in Java - java

I'm trying to write a dynamic programming algorithm for the Longest Common Subsequence.
The return should be the length of this subsequence.
But my algorithm always returns 0. I couldn't find the error.
public static int LCS(String A, String B, int m, int n) {
int table[][] = new int[m + 1][n + 1];
for (int i = 0; i < m; i++) {
table[i][0] = 0;
}
for (int i = 1; i < n; i++) {
table[0][n] = 0;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (A.charAt(i) == B.charAt(j)) {
table[i][j] = table[i - 1][j - 1] + 1;
} else {
table[i][j] = max(table[i][j - 1], table[i - 1][j]);
}
}
}
return table[m][n];
}
private static int max(int a, int b) {
return (a > b) ? a : b;
}
public static void main(String args[]) {
Scanner in = new Scanner(System.in);
System.out.println("Your input words:\n");
String x = in.nextLine();
String y = in.nextLine();
in.close();
int m = x.length();
int n = y.length();
System.out.println("Length of LCS is " + LCS(x, y, m, n));
}

Looks like you implemented this algorithm, but have a few errors:
Your loops should be 1..m and 1..n inclusive, meaning you need to change < to <=.
charAt() is zero-based, so you need charAt(i - 1) and charAt(j - 1).
These are not errors, but:
The loops to initialize to 0 are unnecessary in Java. table is already initialized to all zeroes by the new operator.
No need to implement max(), since it's already implemented as Math.max().
So, here is the result, using names from the linked article:
public static int LCS(String X, String Y) {
final int m = X.length();
final int n = Y.length();
int[][] C = new int[m + 1][n + 1];
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
if (X.charAt(i - 1) == Y.charAt(j - 1))
C[i][j] = C[i - 1][j - 1] + 1;
else
C[i][j] = Math.max(C[i][j - 1], C[i - 1][j]);
return C[m][n];
}
TEST
System.out.println(LCS("This is a test", "Does it work ok?"));
OUTPUT
5
Here is the matching letters of the longest common subsequence:
This is a test
↑↑↑ ↑ ↑
↓↓↓ ↓ ↓
Does it work ok?

The conditions in the for loops use i < m or j < n.
As a consequence i is never equal to m and j is never equal to n, so table[m][n] is never modified inside these loops, right?
The value returned is the value at table[m][n] which is never modified: 0.

Related

I implemented this merge sort algorithm into java and it didn't work

This my introductory course to Algorithms, and I understand algorithms and I can design them, but this is the first time I tryed to apply it in java code, please tell me what i did wrong in this code for it to not work properly
I'm trying to apply this algorithm ,
this is the rest of the algorithm
This is my main void:
int[] A = { 5, 2, 4, 7, 1, 3, 2, 6 };
int p = 1;
int r = A.length;
Main obi = new Main ();
obi.mergeSort (A, p, r);
my first function:
public void mergeSort (int[]A, int p, int r) {
if (p < r){
int q = (p + r) / 2;
mergeSort (A, p, q);
mergeSort (A, q + 1, r);
merge (A, p, q, r);
}
}
my second function:
public void merge (int[]A, int p, int q, int r) {
int n1 = q - p + 1;
int n2 = r - q;
int L[] = new int[n1];
int R[] = new int[n2];
for (int i = 0; i < n1; i++){
L[i] = A[p + i - 1];
}
for (int j = 0; j < n2; j++){
R[j] = A[q + j];
}
int i = 0;
int j = 0;
for (int k = p - 1; k < r; k++){
if (L[i] <= R[j]){
A[k] = L[i];
i = i + 1;
} else {
A[k] = R[j];
j = j + 1;
}
}
}
And this is the error message I got:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
at Main.merge(Main.java:44)
at Main.mergeSort(Main.java:19)
at Main.mergeSort(Main.java:17)
at Main.mergeSort(Main.java:17)
at Main.main(Main.java:10)
I tried to let L[] and R[] have length n1+1 and n2+1 but it didn't help.
There are multiple problems in your code:
the arguments r and q should be the index of the first element in the slice, hence int p = 0; in main() instead of 1, and the index past the last element in the slice, hence correctly int r = A.length;
With this convention, there is no need for confusing and error prone +1/-1 adjustments in the merge and mergeSort methods.
Furthermore, the merging loop in merge should be modified to avoid accessing L[i] or R[j] when the corresponding index reaches the length of the array.
Here is a modified version:
...
int[] A = { 5, 2, 4, 7, 1, 3, 2, 6 };
int p = 0;
int r = A.length;
Main obi = new Main();
obi.mergeSort(A, p, r);
...
public void mergeSort(int[]A, int p, int r) {
if (r - p > 1) {
int q = p + (r - p) / 2;
mergeSort(A, p, q);
mergeSort(A, q, r);
merge(A, p, q, r);
}
}
public void merge(int[]A, int p, int q, int r) {
int n1 = q - p;
int n2 = r - q;
int L[] = new int[n1];
int R[] = new int[n2];
for (int i = 0; i < n1; i++) {
L[i] = A[p + i];
}
for (int j = 0; j < n2; j++) {
R[j] = A[q + j];
}
int i = 0;
int j = 0;
for (int k = p; k < r; k++) {
if (i < n1 && (j >= n2 || L[i] <= R[j])) {
A[k] = L[i];
i = i + 1;
} else {
A[k] = R[j];
j = j + 1;
}
}
}
Note that the merge method can be simplified as the elements of the second half never get overwritten before they are moved into their final spot, so there is no need to save them:
public void merge(int[]A, int p, int q, int r) {
int n1 = q - p;
int L[] = new int[n1];
for (int i = 0; i < n1; i++) {
L[i] = A[p + i];
}
int i = 0;
int j = q;
int k = p;
while (i < n1) {
if (j >= r || L[i] <= A[j])
A[k++] = L[i++];
else
A[k++] = A[j++];
}
}
Either L or R finishes first.
for (int k = p - 1; k < r; k++){
if (L[i] <= R[j]){
A[k] = L[i];
i = i + 1;
} else {
A[k] = R[j];
j = j + 1;
}
}
Lets say L finishes first then i becomes L.length+1 so that when the next iteration occurs L[i] is invalid as its one past the end.

HackerRank closest number

Given a list of unsorted integers, find the pair of elements that have the smallest absolute difference between them. If there are multiple pairs, find them all.
My reasoning was to compare each: arr[j] - arr[i] with lowest and if it is smaller or equal to that, add that value to the array lowest, but it's not working.
Code:
static int[] closestNumbers(int[] arr) {
int lowest = arr[1] - arr[0];
int lowestArray[] = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (Math.abs(arr[j] - arr[i]) < lowest) {
lowest = Math.abs(arr[j] - arr[i]);
}
}
}
for (int i = 0; i < arr.length; i++) {
if (arr[i] == lowest) {
lowestArray[i] = arr[i];
}
}
return lowestArray;
}
Below is the required code:-
import java.util.*;
class GFG
{
// Returns minimum difference between
// any two pair in arr[0..n-1]
static void printMinDiffPairs(int arr[], int n)
{
if (n <= 1)
return;
// Sort array elements
Arrays.sort(arr);
// Compare differences of adjacent
// pairs to find the minimum difference.
int minDiff = arr[1] - arr[0];
for (int i = 2; i < n; i++)
minDiff = Math.min(minDiff, arr[i] - arr[i-1]);
// Traverse array again and print all pairs
// with difference as minDiff.
for ( int i = 1; i < n; i++)
{
if ((arr[i] - arr[i-1]) == minDiff)
{
System.out.print("(" + arr[i-1] + ", "
+ arr[i] + ")," );
}
}
}
// Driver code
public static void main (String[] args)
{
int arr[] = {5, 3, 2, 4, 1};
int n = arr.length;
printMinDiffPairs(arr, n);
}
}
Does above program handle duplicates?
The cases like {x, x, x} are not handled by above program. For this case, the expected output (x, x), (x, x), (x, x), but above program prints (x, x), (x, x)
The problem is that when you initialize your lowest array like this,
int[] lowest = new int[arr.length];
you are actually initializing it with zeros. Also, whenever you are taking arr[j]-arr[i], it will always be greater than or equal to zero (since your array is sorted in an ascending fashion), leading to incorrect results because the following if statement,
if(Math.abs(arr[j] - arr[i]) <= lowest[l]) {
lowest[l] = Math.abs(arr[j] - arr[i]);
}
will never execute for differences greater than 0.
Initialize the lowest array like so,
for(int i=0;i<lowest.length;i++){
lowest[i] = Integer.MAX_VALUE;
}
Also, your outer loop for the i variable starts with i=1, but it should start with i=0
static int[] closestNumbers(int[] arr) {
int minAbs = Integer.MAX_VALUE;
Arrays.sort(arr);
int[] out = new int[(arr.length) + 3];
int j = 0;
for (int i = 1; i < arr.length; i++) {
minAbs = Math.min(minAbs, Math.abs(arr[i] - arr[i - 1]));
}
for (int i = 1; i < arr.length; i++) {
if(minAbs ==Math.abs(arr[i] - arr[i - 1])) {
out[j++]=arr[i - 1];
out[j++]=arr[i];
}
}
int[] tem= new int[j];
for(int i=0; i<j;i++) {
tem[i]=out[i];
}
return tem;
}

Merge sort implementation questions in Java

I'm in an algorithms course and am learning about merge sort. Our professor recommended we try to implement the pseudo code provided in the book.
Am I correct in using Integer.MAX_VALUE as a sentinel value when
sorting an array of integers (used in lines 8 & 9 in the Merge
method pseudo code below)?
For line 2 of the Merge-Sort pseudo code method, is it correct to code that in Java using Math.ceil() like I did? (Edit: It's actually floor and I updated my code to reflect this.)
If you see any other mistakes please let me know!
Here is the pseudo code the book gives for merge sort.
And, here is how I coded it in Java:
public void mergeSort(int[] arrNums, int p, int r) {
if (p < r) {
int q = (p + r) / 2;
mergeSort(arrNums, p, q);
mergeSort(arrNums, q + 1, r);
merge(arrNums, p, q, r);
}
}
public void merge(int[] arrNums, int p, int q, int r) {
int nOne = q - p + 1;
int nTwo = r - q;
int[] arrLeft = new int[nOne + 1];
int[] arrRight = new int[nTwo + 1];
for (int i = 0; i < nOne; i++) {
arrLeft[i] = arrNums[p + i - 1];
}
for (int j = 0; j < nTwo; j++) {
arrRight[j] = arrNums[q + j];
}
arrLeft[nOne] = Integer.MAX_VALUE;
arrRight[nTwo] = Integer.MAX_VALUE;
// Tracks arrLeft index
int i = 0;
// Tracks arrRight index
int j = 0;
for (int k = p; k < r; k++) {
if (arrLeft[i] <= arrRight[j]) {
arrNums[k] = arrLeft[i];
i++;
} else {
arrNums[k] = arrRight[j];
j++;
}
}
}
The last for loop in your merge method, variable k should start from p - 1:
for (int k = p - 1; k < r; k++) {
if (arrLeft[i] <= arrRight[j]) {
arrNums[k] = arrLeft[i];
i++;
} else {
arrNums[k] = arrRight[j];
j++;
}
}
Pseudo code in many text books likes to start array index from 1, so here you need to subtract it by 1.
I implemented it a few days ago, if someone will be interested.
private static void mergeSort(double[] arr, int start, int end){
if(start < end){
int mid = ( start + end ) / 2;
mergeSort(arr, start, mid);
mergeSort(arr, mid + 1, end);
Merge(arr, start, mid, end);
}
}
private static void Merge(double[] arr, int start, int mid, int end){
double[] leftArray = new double[mid - start + 2];
double[] rightArray = new double[end - mid + 1];
for(int i = start; i <= mid; i++ )
leftArray[i - start] = arr[i];
for (int i = mid + 1; i <= end; i++ )
rightArray[i - mid - 1] = arr[i];
leftArray[mid - start + 1] = Double.POSITIVE_INFINITY;
rightArray[end - mid] = Double.POSITIVE_INFINITY;
int leftIndex = 0, rightIndex = 0;
for (int k = start; k <= end; k++){
if(leftArray[leftIndex] <= rightArray[rightIndex])
arr[k] = leftArray[leftIndex++];
else
arr[k] = rightArray[rightIndex++];
}
}

Merge sort for string getting null pointer exception?

void MERGE(String[]A, int p, int q, int r) {
int n1 = q - p + 2;
int n2 = r - q + 1;
String[] L = new String[n1];
String[] R = new String[n2];
int i,j;
for (i = 0; i < L.length; i++) {
L[i] = A[p+i];
}
for (j = 0; i < R.length; j++) {
R[j] = A[q+j+1];
}
L[n1-1] = "";
R[n2-1] = "";
i = 0;
j = 0;
for (int k = p; k <= r; k++) {
if (L[i].compareToIgnoreCase(R[j]) < 0) {
A[k] = L[i];
i++;
}
else {
A[k] = R[j];
j++;
}
}
}
public void MERGE_SORT(String[] A, int p, int r) {
if (p < r) {
int q = (p+r)/2;
MERGE_SORT(A, p, q);
MERGE_SORT(A, q+1, r);
MERGE(A, p, q, r);
}
}
This algorithm was originally for integers this how I changed it to sort strings. I get a NullPointerException. The problem seems to be at the compareToIgnoreCase() line. is this even how you implement mergesort for strings?
public static void main(String[] args) {
String[] sA = {"Jack", "John", "Mike", "Moss", "Xo"};
Sort ob = new Sort();
ob.MERGE_SORT(sA, 0, sA.length - 1);
}
The second for-loop in method MERGE uses the wrong variable i (instead of j) for checking the upper bound (i < R.length). It should be:
for (j = 0; j < R.length; j++) {
R[j] = A[q+j+1];
}
Apart from that, there are two other issues in the code:
(1) The for-loops for initializing L and R should run to L.length - 1 respectively R.length - 1:
for (i = 0; i < L.length - 1; i++) {
L[i] = A[p + i];
}
for (j = 0; j < R.length - 1; j++) {
R[j] = A[q + j + 1];
}
(2) You are using a sentinel in L and R at the last place to guarantee that the merging for-loop never exceeds the arrays. This sentinel should be larger than the largest possible value in the array. In case of an int[] this could be Integer.MAX_VALUE (which is just as large as the largest possible element, but this might be acceptable). But as you have arrays of String, you'd need the largest possible String value. You are using the empty String ("") instead, which is the smallest possible String:
L[n1 - 1] = "";
R[n2 - 1] = "";
For testing, you can use something like "ZZZ", but you should rewrite the merging algorithm to work without sentinel:
L[n1 - 1] = "ZZZ";
R[n2 - 1] = "ZZZ";

Find longest repeating substring with length between x and y

Given a string : "blablafblafbla" and 2 limits : x=3, y=5
I want to find the longest repeating substring that has the length between x and y.If there are many, the first one
In my example that would be "blaf"
Several questions:
1. is it easier to use regex?
2.I know how to find the longest substring but where do i have to put the conditions for it to be between x and y?
public static String longestDuplicate(String text)
{
String longest = "";
for (int i = 0; i < text.length() - 2 * longest.length() * 2; i++)
{
OUTER: for (int j = longest.length() + 1; j * 2 < text.length() - i; j++)
{
String find = text.substring(i, i + j);
for (int k = i + j; k <= text.length() - j; k++)
{
if (text.substring(k, k + j).equals(find))
{
longest = find;
continue OUTER;
}
}
break;
}
}
return longest;
}
The code you provide is an extremely inefficient way to solve the problem you have. I would implement the solution using Rabin-Karp or some other rolling hash algorithm and this will enable you to solve your problem with complexity O((y-x) * L).
You can't use regular expressions here- they are meant to solve copletely different tasks.
As for your question on how to use your solution to find longest substring with length between x and y, simply modify the loop over j to only consider values that are in the interval [x, y]. Here is how you can do that.
for (int j = Math.max(longest.length() + 1, x) ; j * 2 < text.length() - i && j < y; j++)
EDIT: to find the longest substring, reverse the for cycle:
for (int j = Math.min((text.length() - i -1)/2, y) ; j > longest.length() && j >=x; j--)
public static int commonPrefix (String string, int x, int y)
{
int l = string.length ();
int n = 0;
int oy = y;
while (x < oy && y < l && string.charAt (x) == string.charAt (y))
{
n++; x++; y++;
}
return n;
}
public static String longestRepeatingSubstring (
String string, int minLength, int maxLength)
{
String found = null;
int l = string.length ();
int fl = minLength;
for (int x = 0; x < l - fl * 2; x++)
for (int y = x + 1; y < l - fl; y++)
{
int n = commonPrefix(string, x, y);
if (n >= maxLength)
return string.substring(x, x + maxLength);
if (n > fl)
{
found = string.substring (x, x + n);
fl = n;
}
}
return found;
}
public static void main(String[] args) {
System.out.println (longestRepeatingSubstring ("blablafblafblafblaf", 3, 5));
}
Here is a clunky implementation with regex:
//import java.util.regex.*;
public static String longestRepeatingSubstring (String string, int min, int max)
{
for (int i=max; i>=min; i--){
for (int j=0; j<string.length()-i+1; j++){
String substr = string.substring(j,j+i);
Pattern pattern = Pattern.compile(substr);
Matcher matcher = pattern.matcher(string);
int count = 0;
while (matcher.find()) count++;
if (count > 1) return substr;
}
}
return null;
}
public static void main(String[] args) {
System.out.println (longestRepeatingSubstring ("blablafblafbla", 3, 5));
}
public static int getCount(String string , String subString){
int count = 0;
int fromIndex = 0;
do{
if(string.indexOf(subString, fromIndex) != -1){
count++;
fromIndex = string.indexOf(subString, fromIndex);
}
}while(fromIndex == string.length()-1);
return count;
}
public static String longestRepeatingSubstring (int min,int max , String string){
Vector substrs = new Vector();
Vector substrs_length = new Vector();
for (int i=min; i<=max; i++){
for (int j=0; j<string.length()-i+1; j++){
String substr=string.substring(j, i+j);
System.out.println(substr);
if (substrs.indexOf(substr) == -1){
int count =getCount(string, substr);
if (count != 0) {
substrs.addElement(substr);
substrs_length.addElement(count);
}
}
}
}
int maxLength = 0;
int index = -1;
for(int i = 0 ; i < substrs_length.size() ; i++){
int length = (int) substrs_length.elementAt(i);
if(length > maxLength){
maxLength = length;
index = i;
}
}
return (String) substrs.elementAt(index);
}
public static void main(String [] arg){
System.out.print(longestRepeatingSubstring(3, 5, "blablafblafbla"));
}

Categories