Divide Two Integers and Sub String with Concatenation of All Words

Let’s continue…

Problem Statement:

Divide Two Integers

Given two integers dividend and divisor, divide two integers without using multiplication, division and mod operator.

Return the quotient after dividing dividend by divisor.

The integer division should truncate toward zero.

Example 1:

Input: dividend = 10, divisor = 3
Output: 3

Example 2:

Input: dividend = 7, divisor = -3
Output: -2

Note:

  • Both dividend and divisor will be 32-bit signed integers.
  • The divisor will never be 0.
  • Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231,  231 − 1]. For the purpose of this problem, assume that your function returns 231 − 1 when the division result overflows.

Solution:

Solution is found here: https://leetcode.com/problems/divide-two-integers/discuss/13407/Detailed-Explained-8ms-C%2B%2B-solution

This is a problem which aims at testing your understanding about how integers are stored in a modern computer. In this problem, since we cannot use *, /, or %, so we can utilize bit operations.

The general idea is like we first convert both the dividend and divisor to positive numbers, then the quotient should be the number of  maximum times when the dividend can subtract divisor without becoming a negative number.

As for the sign, we can simply save the sign when we convert the numbers to positive numbers, before we return the final result, we can judge whether the final answer should be positive or negative. The only problem we need to care about is the range of int value, which is [-2^31 ~ 2^31 – 1], which is [-2147483648 ~ 2147483647]. If we get the overflow, such as the dividend is -2^31, while the divisor is -1, or divisor is 0, then we have to return 2^31 – 1. This is a special case we need to care about.

==========================================================================

Bit operations are simple, we can improve our skills with some practice:

Operator Symbol Form Operation
left shift << x << y all bits in x shifted left y bits
right shift >> x >> y all bits in x shifted right y bits
bitwise NOT ~ ~x all bits in x flipped
bitwise AND & x & y each bit in x AND each bit in y
bitwise OR | x | y each bit in x OR each bit in y
bitwise XOR ^ x ^ y each bit in x XOR each bit in y

I want to go through all of them before we move forward.

Note: In the following examples, we will generally be working with 4-bit binary values. This is for the sake of convenience and keeping the examples simple. In C++, the number of bits used will be based on the size of the data type (8 bits per byte).

Left shift and right shift

The bitwise left shift (<<) operator shifts bits to the left. The left operand is the expression to shift, and the right operand is an integer number of bits to shift by. So when we say 3 << 1, we are saying “shift the bits in the literal 3 left by 1 place”.

For example, consider the number 3, which is binary 0011:

3 = 0011
3 << 1 = 0110 = 6
3 << 2 = 1100 = 12
3 << 3 = 1000 = 8

As you can see, if we have sufficient bits, when we left shift 1 bit from the original integer, the integer value will double. For bitwise right shift, it is the reversed result.

Note that in the third case, we shifted a bit off the end of the number! Bits that are shifted off the end of the binary number are lost forever.

Bitwise NOT

The bitwise NOT operator (~) is perhaps the easiest to understand of all the bitwise operators. It simply flips each bit from a 0 to a 1, or vice versa. Note that the result of a bitwise NOT is dependent on what size your data type is!

Assuming 4 bits:
4 = 0100
~4 = 1011 = 11 (decimal)

Bitwise AND, OR, and XOR

Bitwise AND (&) and bitwise OR (|) work similarly to their logical AND and logical OR counterparts. However, rather than evaluating a single boolean value, they are applied to each bit! For example, consider the expression 5 | 6. In binary, this is represented as 0101 | 0110. To do (any) bitwise operations, it is easiest to line the two operands up like this:

0 1 0 1 // 5
0 1 1 0 // 6

and then apply the operation to each column of bits. If you remember, logical OR evaluates to true (1) if either the left or the right or both operands are true (1). Bitwise OR evaluates to 1 if either bit (or both) is 1. Consequently, 5 | 6 evaluates like this:

0 1 0 1 // 5
0 1 1 0 // 6
-------
0 1 1 1 // 7

Our result is 0111 binary (7 decimal).

Bitwise AND works similarly. Logical AND evaluates to true if both the left and right operand evaluate to true. Bitwise AND evaluates to true only if both bits in the column are 1. Similarly, we can do the same thing to compound AND expressions, such as 1 & 3 &7. If all of the bits in a column are 1, the result of that column is 1.

0 0 0 1 // 1
0 0 1 1 // 3
0 1 1 1 // 7
--------
0 0 0 1 // 1

The last operator is the bitwise XOR (^), also known as exclusive or. When evaluating two operands, XOR evaluates to true (1) if one and only one of its operands is true (1). If neither or both are true, it evaluates to 0. Consider the expression 6 ^ 3:

0 1 1 0 // 6
0 0 1 1 // 3
-------
0 1 0 1 // 5

It is also possible to evaluate compound XOR expression column style, such as 1 ^ 3 ^ 7. If there are an even number of 1 bits in a column, the result is 0. If there are an odd number of 1 bits in a column, the result is 1.

0 0 0 1 // 1
0 0 1 1 // 3
0 1 1 1 // 7
--------
0 1 0 1 // 5

Bitwise assignment operators

As with the arithmetic assignment operators, C++ provides bitwise assignment operators in order to facilitate easy modification of variables.

Operator Symbol Form Operation
Left shift assignment <<= x <<= y Shift x left by y bits
Right shift assignment >>= x >>= y Shift x right by y bits
Bitwise OR assignment |= x |= y Assign x | y to x
Bitwise AND assignment &= x &= y Assign x & y to x
Bitwise XOR assignment ^= x ^= y Assign x ^ y to x

For example, instead of writing x = x << 1;, you can write x <<= 1;.

============================================================================

OK, so let’s go to our code… I will add comments for the code.

Time Complexity: O(log n) n is the number of how many times we need to subtract dividend in order to make it negative.

Space Complexity: O(1)



Problem Statement:

Sub string with concatenation of all words

You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.

Example 1:

Input:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
Output: [0,9] Explanation: Substrings starting at index 0 and 9 are "barfoor" and "foobar" respectively. The output order does not matter, returning [9,0] is fine too.

Example 2:

Input:
  s = "wordgoodstudentgoodword",
  words = ["word","student"]
Output: []

Solution:

 

The general idea for this solution: We allocate two hash tables, in the first hash table counts, we store the number of all the unique words from vector words. We use every single word as the key, and the frequency of that word is the value.

Then we scan the string s, we compare each sub string, and test whether all the words in the sub string matches the number of words from the hash table counts, if it matches, then we store the first index of this sub string, if not, we move forward, and test the next sub string.

We can use the second hash table detect to do the comparing string job, whenever in the sub string we encounter a word which is from words, we store them in the detect hash table, and increase its frequency by 1, if the frequency is greater than the number of words in counts, we can safely say this sub string does not work. We can break the loop and move forward. Another situation is we cannot find a word from the sub string which is the same as the word from vector words, we can move forward as well.

Look at the picture below, we are pretty sure in this case, the length of sub string should be 6 (2 * 3), so we need to compare every sub string until the end of the string s.

Let’s see a live demo for the code:

If we have the string s and words like the picture below, we first store all the words from vector words to hash table 1 (the value is frequency of each words):

30_2

Then we compare the first sub string, since “foo” is in hash table 1, we update the hash table 2 with the new word “foo” and it occurs once, which means its frequency is less than or equal to the frequency of “foo” in hash table 1. Then we can keep moving.

30_3

We compare the second sub string, which is “foo” as well. So we update the frequency of “foo” in hash table 2, since the frequency is still equal to the frequency from table 1, and the final sub string contains less than 3 words, we keep moving.

30_4

OK, this time, it’s “foo” again, and the frequency is greater than the frequency from hash table 1, we have to break here, cause this sub string is not valid.

30_5

Let’s see the code:

Time Complexity: O(n*m) n is the number of characters in the string s, and m is the number of words in the vector words;

Space Complexity: O(m)

Actually, this is not the most optimal solution, currently, we iterate characters from s one by one, which is not good. We can optimize the time complexity and change it to O(n). A beautiful solution is here: https://leetcode.com/problems/substring-with-concatenation-of-all-words/discuss/13656/An-O(N)-solution-with-detailed-explanation%E3%80%82

That’s all for today…

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s