Friday, November 24, 2023

 

Decode ways:

A message containing letters from A-Z can be encoded into numbers using the following mapping:

'A' -> "1"

'B' -> "2"

...

'Z' -> "26"

To decode an encoded message, all the digits must be grouped then mapped back into letters using the reverse of the mapping above (there may be multiple ways). For example, "11106" can be mapped into:

  • "AAJF" with the grouping (1 1 10 6)
  • "KJF" with the grouping (11 10 6)

Note that the grouping (1 11 06) is invalid because "06" cannot be mapped into 'F' since "6" is different from "06".

Given a string s containing only digits, return the number of ways to decode it.

The test cases are generated so that the answer fits in a 32-bit integer.

 

Example 1:

Input: s = "12"

Output: 2

Explanation: "12" could be decoded as "AB" (1 2) or "L" (12).

Example 2:

Input: s = "226"

Output: 3

Explanation: "226" could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

Example 3:

Input: s = "06"

Output: 0

Explanation: "06" cannot be mapped to "F" because of the leading zero ("6" is different from "06").

 

Constraints:

  • 1 <= s.length <= 100
  • s contains only digits and may contain leading zero(s).

 

class Solution {

    public int numDecodings(String s) {

        Integer count = 0;

        if (isValid(s.substring(0,1)))

            traverse(s.substring(1), count);

        if (s.length() >= 2 && isValid(s.substring(0,2)))

            traverse(s.substring(2), count);

        return count;

    }

    public boolean traverse(String s, Integer count) {

        if (String.isNullOrWhitespace(s)){

            count += 1;

            return true;

        }

        if (isValid(s.substring(0,1)))

            traverse(s.substring(1), count);

        if (s.length() >= 2 && isValid(s.substring(0,2)))

            traverse(s.substring(2), count);

        return count > 0;

    }

    public boolean isValid(String s) {

        if (s.length() == 1 && s.charAt(0) >= '0' && s.charAt(0) <= '9'){

            return true;

        }

        if (s.length() == 2 &&

           (s.charAt(0) > '0' && s.charAt(0) <= '2') &&

           ((s.charAt(0) == '1' && s.charAt(1) >= '0' && s.chartAt(1) <= '9') ||

            (s.charAt(0) == '2' && s.chartAt(1) >= '0' && s.chartAt(1) <= '6')) {

            return true;

        }

        return false;

    }

}

 

Thursday, November 23, 2023

 #codingexercise 

Given an integer array arr, in one move you can select a palindromic subarray arr[i], arr[i+1], ..., arr[j] where i <= j, and remove that subarray from the given array. Note that after removing a subarray, the elements on the left and on the right of that subarray move to fill the gap left by the removal.

 

Return the minimum number of moves needed to remove all numbers from the array.

 

 

import java.util.*;

class Solution {

    public int minimumMoves(int[] arr) {

        int N = arr.length;

        int max = 1;

        int min = Integer.MAX_VALUE;

        List<Integer> A = new ArrayList<>();

        for (int i = 0; i < arr.length; i++) A.add(arr[i]);

        int count = 0;

        while(A.size() > 0) {

           boolean hasPalindrome = false;

           List<Integer> elements = new ArrayList<>();

           for (int i = 0; i < (1<<N); i++) {

              

               List<Integer> combination = new ArrayList<>();

                for (int j = 0; j < A.size(); j++) {

                  if ((i & (1 << j)) > 0) {

                    combination.add(j);

                  }

                }

                if (isPalindrome(A, combination) && (combination.size() > max) && getCharactersToRemove(A, combination) < min) {

                      hasPalindrome = true;

                      max = combination.size();

                      elements = new ArrayList<>(combination);               

                      if (getCharactersToRemove(A, combination) == 0) { break;}

                } else {

                    // System.out.println("A: " + print(A) + " Elements: " + print(elements) + " Combination: " + print(combination) + "isPalindrome=" + String.valueOf(isPalindrome(A, combination)) + " getCharsToRemove=" + getCharactersToRemove(A, combination) + " min = " + min);

                }

           }           

           if (!hasPalindrome) {

               count += 1;

               A.remove(A.size() - 1);

           } else {

               count += getCharactersToRemove(A, elements) + 1;

               A = removeCharacters(A, elements);

               // System.out.println("Removing " + count + " characters at indices:" + print(elements) + " and remaining elements: " + print(A));

               // elements = new ArrayList<>();

               max = 1;

               min = Integer.MAX_VALUE;

           }

        }

        return count;

    }

    public boolean isPalindrome(List<Integer> A, List<Integer> combination) {

        int start = 0;

        int end = combination.size()-1;

        while (start <= end) {

            if (A.get(combination.get(start)) != A.get(combination.get(end))) {

                return false;

            }

            start++;

            end--;

        }

        return true;

    }

    public int getCharactersToRemove(List<Integer> A, List<Integer> combination){

        if (combination.size() < 2) return 0;

        int start = combination.get(0);

        int end = combination.get(combination.size()-1);

        int count = 0;

        for (int i = start+1; i < end; i++) {

            if (combination.contains(i) == false){

                count++;

            }

        }

        return count;

    }

    public List<Integer> removeCharacters(List<Integer> A, List<Integer> combination) {

        int start = combination.get(0);

        int end = combination.get(combination.size()-1);

        List<Integer> result = new ArrayList<>();

        if (start > 0){

            result.addAll(A.subList(0, start));

        }

        if (end < A.size() - 1) {

            result.addAll(A.subList(end + 1,A.size()));

        }

        return result;

    }

    public String print(List<Integer> elements){

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < elements.size(); i++) {

            sb.append(elements.get(i) + " ");

        }

        return sb.toString();

    }

}

Wednesday, November 22, 2023

#codingexercise

Given an array of varying heights above sea level for adjacent plots, and the array of water levels on consecutive days, find the number of islands. An island is a slice of array such that plots adjacent to the boundary of the island are under water.

class Solution {

public int[] solution(int[] A, int[] B) {

    return Arrays

    .stream(B)

    .map(water -> IntStream

         .range(0, N)

         .filter(j -> (A[j] > water) && (j == N - 1 || A[j + 1] <= water))

         .map(i -> 1)

         .sum()) .toArray();

}

}

For example, given the following arrays A and B:

    A[0] = 2    B[0] = 0

    A[1] = 1    B[1] = 1

    A[2] = 3    B[2] = 2

    A[3] = 2    B[3] = 3

    A[4] = 3    B[4] = 1

Solution:

result[0] = 1

result[1] = 2

result[2] = 2

result[3] = 0

result[4] = 2

 

For a water level, the number of islands is just the sum of changes in the number of islands as the water level is decreasing.

Optimized solution:

class Solution {

public int[] solution(int[] A, int[] B) {

     int limit = Math.max(maxLevel(A), maxLevel(B));

     int[] island = new int[limit + 2];

     IntStream.range(0, A.length - 1)

                       .filter( j -> A[j]  > A[j+1])

                      .forEach(j ->  {

                                       island[A[j]] += 1;

                                       island[A[j + 1]] -= 1;

                       });

     island[A[A.length-1]] += 1;

     IntStream.range(-limit, 0)

                      .forEach(i -> island[-i] += island[-i+1]);

     return Arrays.stream(B).map(water -> island[water + 1]).toArray();

}

public int maxLevel(int[] A) {

       return Arrays.stream(A).max().getAsInt();

}

}

 

// before cumulation

island[0] = 0

island[1] = -1

island[2] = 0

island[3] = 2

island[4] = 0

// after cumulation

island[0] = 1

island[1] = 1

island[2] = 2

island[3] = 2

island[4] = 0

 

result[0] = 1

result[1] = 2

result[2] = 2

result[3] = 0

result[4] = 2

 

 https://1drv.ms/w/s!Ashlm-Nw-wnWhNMJfj4kcj7MFloLBw?e=ech2Ur

Tuesday, November 21, 2023

 

Given an array of varying heights above sea level for adjacent plots, and the array of water levels on consecutive days, find the number of islands. An island is a slice of array such that plots adjacent to the boundary of the island are under water.

class Solution {

public int[] solution(int[] A, int[] B) {

    return Arrays

    .stream(B)

    .map(water -> IntStream

         .range(0, N)

         .filter(j -> (A[j] > water) && (j == N - 1 || A[j + 1] <= water))

         .map(i -> 1)

         .sum()) .toArray();

}

}

For example, given the following arrays A and B:

    A[0] = 2    B[0] = 0

    A[1] = 1    B[1] = 1

    A[2] = 3    B[2] = 2

    A[3] = 2    B[3] = 3

    A[4] = 3    B[4] = 1

Solution:

result[0] = 1

result[1] = 2

result[2] = 2

result[3] = 0

result[4] = 2

 

For a water level, the number of islands is just the sum of changes in the number of islands as the water level is decreasing.

Optimized solution:

class Solution {

public int[] solution(int[] A, int[] B) {

     int limit = Math.max(maxLevel(A), maxLevel(B));

     int[] island = new int[limit + 2];

     IntStream.range(0, A.length - 1)

                       .filter( j -> A[j]  > A[j+1])

                      .forEach(j ->  {

                                       island[A[j]] += 1;

                                       island[A[j + 1]] -= 1;

                       });

     island[A[A.length-1]] += 1;

     IntStream.range(-limit, 0)

                      .forEach(i -> island[-i] += island[-i+1]);

     return Arrays.stream(B).map(water -> island[water + 1]).toArray();

}

public int maxLevel(int[] A) {

       return Arrays.stream(A).max().getAsInt();

}

}

 

// before cumulation

island[0] = 0

island[1] = -1

island[2] = 0

island[3] = 2

island[4] = 0

// after cumulation

island[0] = 1

island[1] = 1

island[2] = 2

island[3] = 2

island[4] = 0

 

result[0] = 1

result[1] = 2

result[2] = 2

result[3] = 0

result[4] = 2

 

 

Monday, November 20, 2023

Azure Data Factory and its programmability.

 As with any resource in the Azure public cloud that can be provisioned with an ARM template, Azure Data Factory provides several way to automate its operations such as with the Command Line Interface, Its RESTful APIs and SDK in various languages. Since calling over the public internet with REST APIs allows the caller and the target to be in different networks, this article suggests some API calls for the frequently encountered usages. Specifically, it calls out two use cases: 1) to trigger a pipeline job  and 2) to link external storage accounts so that new pipelines can be created.

Use case 1: Triggering a pipeline job:

This is done with the help of a

POST https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataFactory/factories/{factoryName}/pipelines/{pipelineName}/createRun?api-version=2018-06-01

Request

With the parameters as

factoryName – for the name of the ADF

pipelineName – for the name of the pipeline within the ADF

resourceGroupName – for the name of the resource group

subscription id – for the id of the subscription

api-version – this could be set to 2018-06-01

The Authorization header will have the Bearer token corresponding to identity with which to execute the request.

The request body can take additional parameters as:
{

  "OutputBlobNameList": [

    "exampleoutput.csv"

  ]

}

The result will usually have a runId with a GUID as value.

Use case 2: Creating a linked service to an external Amazon S3 compatible storage such as ones hosted on-premise.

This is done with the help of a

PUT https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataFactory/factories/{factoryName}/linkedservices/{linkedServiceName}?api-version=2018-06-01

request

again, with the same parameters as in use case 1.

The request header will carry an authorization with Bearer token of the identity of the caller.

The request body will carry the details for the corresponding service.

{

  "properties": {

    "type": "AzureStorage",

    "typeProperties": {

      "connectionString": {

        "type": "SecureString",

        "value": "DefaultEndpointsProtocol=https;AccountName=examplestorageaccount;AccountKey=<storage key>"

      }

    }

  }

}

The example here shows AzureStorage but for Amazon S3 compatible storage, an account name, an access key and secret will suffice as payload to the API request above.

The response will be similar to what’s shown below except that it will be for Amazon S3 Compatible storage.

{

  "id": "/subscriptions/12345678-1234-1234-1234-12345678abc/resourceGroups/exampleResourceGroup/providers/Microsoft.DataFactory/factories/exampleFactoryName/linkedservices/exampleLinkedService",

  "name": "exampleLinkedService",

  "type": "Microsoft.DataFactory/factories/linkedservices",

  "properties": {

    "type": "AzureStorage",

    "typeProperties": {

      "connectionString": {

        "type": "SecureString",

        "value": "**********"

      },

      "encryptedCredential": "ew0KICAiVmVyc2lvbiI6ICIyMDE3LTExLTMwIiwNCiAgIlByb3RlY3Rpb25Nb2RlIjogIktleSIsDQogICJTZWNyZXRDb250ZW50VHlwZSI6ICJQbGFpbnRleHQiLA0KICAiQ3JlZGVudGlhbElkIjogIkRGLURPR0ZPT0QtWUFOWkhBTkctV1VfM2FiMTk0NjYtNWUxNi00NzU1LWJlNzktMjI2ZTVmZWU3YzY0Ig0KfQ=="

    }

  },

  "etag": "0a0062d4-0000-0000-0000-5b245bcf0000"

}

Reference:

1.       https://learn.microsoft.com/en-us/azure/data-factory/connector-amazon-s3-compatible-storage?tabs=data-factory

2.       https://learn.microsoft.com/en-us/rest/api/datafactory/linked-services/create-or-update?view=rest-datafactory-2018-06-01&tabs=HTTP#linkedservices_create

3.       Prior articles on IaC: IaCResolutionsPart41.docx