如何在编程时屏蔽输入法

by Sachin Malhotra

由Sachin Malhotra

取消屏蔽位屏蔽的动态编程 (Unmasking Bitmasked Dynamic Programming)

如何从恐惧变成征服! (How to go from Fearing it to Conquering it!)

There are no coincidences in this world.

这个世界上没有巧合。

— Grand Master Oogway

—奥格威大师

Well, Master Oogway, no disrespect, coincidences do occur and I think they are just God’s way of remaining anonymous. Not just me, Albert Einstein believes this too ?.

好吧,Oogway大师,不要无礼,巧合确实发生了,我认为这只是上帝保持匿名的方式。 不仅仅是我,爱因斯坦(Albert Einstein)也相信吗?

111, that’s not really my lucky number, so to say.

111,可以这么说,这真的不是我的幸运数字

However, this number filled me with joy and ecstasy when I saw it as my rank on the rankings page of the LeetCode contest number 111.

但是,当我在LeetCode竞赛编号111的排名页面上看到自己的排名时,这个数字让我感到欣喜若狂。

What a coincidence!

真是巧合!

That’s the highest ranking in a weekly contest that I’ve achieved so far on the platform. The contest number and the ranking were obviously pure coincidence.

这是到目前为止,我在平台上赢得的每周竞赛中的最高排名。 比赛的次数和排名显然是纯粹的巧合。

The contest usually consists of an Easy problem, 2 Medium level problems, and a Hard problem.

比赛通常包括一个简单问题,两个中等水平问题和一个困难问题。

More often than not, the hard problem is something that requires a lot of algorithmic knowledge and prior practice to be able to pull off during the contest. This contest’s final problem was no exception to this.

通常,难题是需要很多算法知识和以前的实践才能在比赛中取得成功的难题。 比赛的最后一个问题也不例外。

Why do I say it was very hard? Have a look at the number of people who were able to solve it during the contest.

为什么我说很难? 查看在比赛期间能够解决该问题的人数。

As the title of the article suggests, this problem was to be solved using Bit Masking based Dynamic Programming.

如文章标题所示,此问题将使用基于位掩码的动态编程来解决。

Dynamic Programming is one of the most dreaded algorithmic domains out there. It requires a lot of practice to develop intuition about a dynamic programming-based solution to a problem. I’ve always considered it to be an enhancement to a recursive solution to a problem. The main idea behind dynamic programming in layperson’s terms is:

动态编程是最可怕的算法领域之一。 开发有关基于动态编程的问题解决方案的直觉需要大量的实践。 我一直认为它是对问题的递归解决方案的增强。 用非专业人员的观点进行动态编程的主要思想是:

Avoid repeated computations by caching the results.

通过缓存结果避免重复计算。

Bitmasking as a topic in computer programming is something that I have (and I’m sure countless of other developers out there have as well) feared for a long time. This is one of those topics that will surely take you off balance in an interview and get you a rejection.

长期以来 ,我一直担心将位掩码作为计算机编程中的一个话题(而且我敢肯定,还有其他开发人员也对此感到恐惧)。 这是肯定会在面试中失去平衡并被拒绝的主题之一。

Programmers out there generally tend to avoid practicing problems related to this topic simply because it is difficult to build an intuition about it.

那里的程序员通常倾向于避免练习与该主题相关的问题,这仅仅是因为很难建立关于它的直觉。

Optimizations related to bit manipulations occur in the most unexpected of places. With some practice, I have been able to overcome the fear, so to say, of working on bitmasking-based programming problems.

与位操作有关的优化发生在最意想不到的地方。 随着一些练习,我已经能够克服恐惧,如此说来,在bitmasking基础的编程问题的工作。

In this article, apart from describing the solution to the problem I’ve mentioned above, in detail, I will also go over some basics of bitmasking and some programming problems where it can come in handy.

在本文中,除了详细描述我上面提到的问题的解决方案之外,我还将详细介绍一些位掩码的基础知识和一些可以方便使用的编程问题。

As with any new thing you learn, it’s very difficult to retain theoretical concepts related to bitmasking. Retention is the best when it comes via practice. That is the main aim of this article. Let’s quickly look at the table of contents before we kick off the main article.

与您学到的任何新事物一样,保留与位掩码相关的理论概念非常困难。 通过实践,保留是最好的。 这是本文的主要目的。 在开始本篇文章之前,让我们快速查看目录。

获取有点热巧克力,并准备好... (Get a bit of Hot Chocolate and be ready for …)

  • 0️⃣ 1️⃣ What is Bit Manipulation? 0️⃣ 1️⃣

    0️⃣1️⃣什么是位操作? 0️⃣1️⃣

  • The Basics ✍️

    基础知识✍️

  • Counting the Number of set bits

    计数设置位数

  • Masking and Unmasking a specific bit

    屏蔽和取消屏蔽特定位

  • ? Missing Number ?

    ? 缺少号码?

  • ☝️?✌️? Counting Bits ?? ??

    ☝️?✌️? 数位?? ??

  • ⛩ Maximum Product of Word Lengths ⛩

    Word字长的最大乘积⛩

  • Set Representation using Bitmasking

    使用位掩码设置表示

  • ??Partition Array to K Equal Sum Subsets ??

    分区数组为K个相等和子集

  • ? Find the Shortest Superstring ?

    ? 查找最短的超级字符串?

  • Conclusion ??

    结论??

  • References ?

    参考文献?

0️⃣1️⃣什么是位操作? 0️⃣1️⃣ (0️⃣ 1️⃣ What is Bit Manipulation? 0️⃣ 1️⃣)

A bit is to the computing world what an atom is to human life.

对计算机世界来说,原子对人类生活来说有点像。

A bit is essentially the smallest unit of storage in a computer. It’s the only unit that a computer understands.

比特本质上是计算机中最小的存储单元。 这是计算机能够理解的唯一单位。

The only information that a bit can store is formed from two different states: 0️⃣ and 1️⃣. Any sort of computation that a computer performs is basically some form of bit manipulation.

一点只能存储的信息由两个不同的状态形成:0️⃣和1️⃣。 计算机执行的任何类型的计算基本上都是某种形式的位操作。

Let’s look at a Wikipedia definition for bit manipulation:

让我们看一下维基百科 位操作的定义:

Bit manipulation is the act of algorithmically manipulating bits or other pieces of data shorter than a word. Computer programming tasks that require bit manipulation include low-level device control, error detection and correction algorithms, data compression, encryption algorithms, and optimization. For most other tasks, modern programming languages allow the programmer to work directly with abstractions instead of bits that represent those abstractions.

位操作是通过算法处理比单词短的位或其他数据的行为。 需要位操作的计算机编程任务包括低级设备控制,错误检测和纠正算法,数据压缩,加密算法和优化。 对于大多数其他任务,现代编程语言允许程序员直接使用抽象,而不是代表那些抽象的位。

Implementation-wise, one of the most useful and effective low-level optimizations to an algorithm is bit manipulation.

在实现方面,对算法最有用和最有效的低级优化之一是位操作。

In some cases, bit manipulation can bypass looping over a data structure and give manifolds speed improvement. The only downfall to such optimizations is the code readability and maintenance.

在某些情况下,位操作可以绕过数据结构的循环并提高速度。 这种优化的唯一缺点是代码的可读性和维护性。

That is one scary piece of ☠code ☠

那是☠ 代码中的一个令人恐惧的片段☠

基础知识✍️ (The Basics ✍️)

At the heart of bit manipulation are the bit-wise operators:

位操作的核心是按位运算符:

  • And (&)和(&)
  • Or (|)或(|)
  • Not (~)不(〜)
  • XOR (^)异或(^)

These are the fundamental operators using which we can perform some complicated bit manipulation operations. Hence, it’s very important to brush up on these operators and their truth tables before moving onto some more interesting stuff.

这些是基本的运算符,通过它们我们可以执行一些复杂的位操作。 因此,在进入一些更有趣的内容之前,梳理这些运算符及其真值表非常重要。

The truth tables show the results for these operators when they operate on 2 bits represented by A and B. The computer has to deal with much more than just one bit of data.

真值表显示了这些运算符在以AB表示的2位上进行运算时的结果。 计算机必须处理的不仅是一小部分数据。

The data processed by the system is generally in bytes or kilobytes or more. How do these operators work on operands represented by for e.g. 8-bits or 16-bits? In such a scenario, the operations are the same, except that they operate on each bit of the arguments. Let’s consider a simple example to clarify this.

系统处理的数据通常以字节或千字节或更大为单位。 这些运算符如何处理以8位或16位表示的操作数? 在这种情况下,除了对参数的每一位进行操作之外,其他操作都是相同的。 让我们考虑一个简单的例子来阐明这一点。

A = 11101010B = 00110101
+-+-+-+-  AND  -+-+-+-+A & B = 00100000
+-+-+-+-  OR   -+-+-+-+A | B = 11111111
+-+-+-+-  NOT  -+-+-+-+~A    = 00010101
+-+-+-+-  XOR  -+-+-+-+A ^ B = 11011111

In addition to these 4 basic operators, there are two other set of bitwise operators that do come in very handy. Almost all the problems that we will look at in this article will be making use of them to give a huge boost in computational speed. These are the left shift &lt;< and the right shift >> operators.

除了这4个基本运算符外,还有另外两组非常有用的按位运算符。 我们将在本文中讨论的几乎所有问题都将利用它们来极大地提高计算速度。 这些是左移&l吨; <和右sh IFT >>运算符。

Simply speaking, the left shift operator means multiplying a number by 2 and the right shift operator means dividing a number by 2.

简而言之,左移位运算符意味着将数字乘以2,而右移位运算符意味着将数字除以2。

Let’s look at a simple animation to show why these operators are called left shift and right shift respectively.

让我们看一个简单的动画,以说明为什么将这些运算符分别称为left shiftright shift

For demonstrating the left shift operation, we start off with the decimal number 1 and then repeatedly multiply it with 2. As you can see in the binary representation of the resulting numbers, the only 1 in the representation keeps on shifting left one step at a time. That’s why it’s called the left shift operation.

为了演示左移操作,我们从十进制数字1 ,然后将其与2重复乘以。如您在结果数字的二进制表示形式中所见,表示形式中的唯一1一直在向左移动一步。时间。 这就是为什么它被称为左移 操作。

Along the same lines, for demonstrating the right shift operation, we start off with the decimal number 128and then repeatedly divide it by 2. As you can see in the binary representation of the resulting numbers, the only 1 in the representation keeps on shifting right one step at a time. That’s why it’s called the right shift operation.

同样,为了演示右移操作,我们先从十进制数128 ,然后将其重复除以2。如您在结果数的二进制表示中所见,表示中唯一的1保持不变一次正确的一步。 这就是为什么它被称为右移操作。

Now that we are all versed with the basic binary operators, let’s move on to some simple use cases for these operators. We will look at a few examples below. These are not programming problems by themselves, however, they are used a lot as building blocks in a lot of algorithms.

现在我们都熟悉基本的二进制运算符,让我们继续介绍这些运算符的一些简单用例。 我们将在下面看一些示例。 这些本身并不是编程问题,但是,它们在许多算法中被用作构造块。

基本用例 (Basic Use Cases ?)

计算设置位数 (Counting the Number of set bits)

One of the basic utilities for the operators we looked at above is to count the number of bits, set in a given binary representation.

上面介绍的用于运算符的基本实用工具之一是对以给定的二进制表示形式设置的位数进行计数。

This might not seem an important use case right now, but we will be getting into more details later on and then it will start to seem more meaningful. For now, let’s just count the number of set bits as efficiently as possible.

目前,这似乎并不是一个重要的用例,但是稍后我们将详细讨论更多的细节,然后它将开始显得更有意义。 现在,让我们仅尽可能有效地计算设置位数。

The first method that we will look at for this, is a bit intuitive. It makes use of the bitwise AND operator. Starting from the least significant bit, we simply check if the bit at each position is set or not and increment a counter accordingly.

我们将着眼于为这个第一种方法,是有点直观。 它使用按位AND运算符。 从最低有效位开始,我们只需检查每个位置的位是否设置,然后相应地增加一个计数器。

The AND operator returns True iff both the bits are True. Let’s look at the code for this.

AND运算符返回True当且仅当这两个位是True 。 让我们看一下代码。

Yet another way of doing this is by ANDing the number with itself-1 until the number becomes zero. The number of steps taken to reach 0 will be the number of set bits in the original number.

进行此操作的另一种方法是将数字与itself-1进行ANDingANDing ,直到数字变为零为止。 达到0的步数将是原始数字中设置的位数。

The reason this works is that every time we AND the number with itself-1, one bit gets removed from the number. This goes on until the number becomes zero.

之所以起作用,是因为每次我们将数字与itself-1 AND itself-1 ,都会从数字中删除一位。 一直进行到数字变为零为止。

屏蔽和取消屏蔽特定位 (Masking and Unmasking a specific bit)

Suppose we want to mask a specific bit in a binary representation. This simply means turning off the bit or transforming a 1 → 0 . On similar lines, unmasking simply means the reverse operation on a specific bit.

假设我们要屏蔽二进制表示形式中的特定 这仅意味着关闭该位或转换1 → 0 。 在相似的行上, 取消屏蔽仅表示对特定位进行反向操作。

You might be wondering why this is useful at all. One of the most important use cases for masking (or unmasking) a bit is in set related operations.

您可能想知道为什么这很有用。 屏蔽(或取消屏蔽)位的最重要用例之一是与集合相关的操作。

We can represent a set of items as an X-bit integer, Xis the number of items in the set. Masking a bit would mean the removal of that item from the set. For a practical application of this, be patient and read on ?.

我们可以将一组项目表示为X-bit整数, X是该集中的项目数。 掩盖一点意味着从集合中删除该项目。 对于此的实际应用,请耐心阅读。

XOR is a very versatile operator and it is perfect for the task of masking and unmasking operations.

XOR是一种用途非常广泛的运算符,非常适合进行掩膜和非掩膜操作。

1 ^ ? → 00 ^ ? → 1

XOR outputs 1 when both the bits are opposite and a 0 when both the bits are the same.

当两个位都相反时,XOR输出1;而当两个位相同时,则输出0。

Essentially, we can make use of the same masking variable for achieving the set / un-set operation corresponding to a particular bit (the integer in the ? is being called the masking variable).

本质上,我们可以利用相同的掩码变量来实现与特定位相对应的置位/取消设置操作(?中的整数称为掩码变量)。

A ^ (1 << i)

The above operation will lead to masking the bit at index i if originally that bit was set in A and the same operation will lead to unmasking the bit at index i if originally it was unset in A.

上述操作将导致在索引掩蔽位i如果最初该位在设定 A和相同的操作将导致在索引揭露位i如果最初它是在未设置 A

We have already seen how to detect if a bit is set or not when we looked at ways to count the number of set bits in a given binary representation.

当我们研究计数给定二进制表示形式中设置的位数时,我们已经看到了如何检测是否设置了一位。

An important thing to note here is the index. Usually, for data structures in high-level programming languages, whenever we refer to a specific index, we mean the index of a particular element in that data-structure starting from the left end.

这里要注意的重要事项是索引。 通常,对于高级编程语言中的数据结构,每当我们引用特定索引时,都是指该数据结构中特定元素的索引从左端开始。

What we are referring to an index above is from the right end (the least significant bit in the binary representation has the index 0).

上面的索引指的是从右端开始 (二进制表示形式的最低有效位为索引0)。

That’s more than enough of the basics for now. Let’s get to some actual programming problems. This will help solidify what we’ve learned so far in the article and also help build up an intuition for solving problems using bit manipulation in general.

到目前为止,这已经足够了基础知识。 让我们来看一些实际的编程问题。 这将有助于巩固我们到目前为止在本文中所学到的知识,也有助于树立通常使用位操作来解决问题的直觉。

? 缺少号码? (? Missing Number ?)

There are multiple ways of solving this problem. We can sort the given list of numbers and then iterate from 0..n and easily find the missing number. This will give us a O(NlogN) solution.

解决此问题有多种方法。 我们可以对给定的数字列表进行排序,然后从0..n进行迭代,轻松找到丢失的数字。 这将为我们提供O(NlogN)解决方案。

Another way of solving this problem is by making use of a dictionary. We simply add all the elements in our list to a dictionary and then we can simply search for the missing number. This is a linear time solution but it makes use of additional space.

解决此问题的另一种方法是使用字典。 我们只需将列表中的所有元素添加到字典中,然后就可以简单地搜索缺少的数字。 这是一个线性时间解决方案,但它占用了额外的空间。

Let’s look at how we can achieve a O(1) space, O(N) time solution like a boss ? by using bit manipulation.

让我们看看如何像老板一样获得O(1)空间, O(N)时间解决方案? 通过使用位操作。

We will make use the XOR property here to solve this problem. As mentioned before, XOR evaluates to True when the input bits are different and it evaluates to False when presented with the same bits. We are interested in the later scenario. What do you think the following evaluates to?

我们将在这里使用XOR属性来解决此问题。 如前所述,当输入的位不同时,XOR的计算结果为True当输入的位相同时,XOR的计算结果为False 。 我们对后一种情况感兴趣。 您认为以下内容有何评价?

A ^ A

XORing a number with itself gives us 0. That’s the main idea behind our approach here.

XORing数字与自己XORing得到0。这是我们此处方法的主要思想。

So, what we will do is, we will XOR all the numbers in our list. Let’s call the value we get after this as A .

因此,我们要做的是,将对列表中的所有数字进行异或运算。 让我们将在此之后获得的值称为A

We will XOR all the numbers from 0..n together. Let’s call this value, B.

我们将所有从0..n的数字进行异或运算。 我们将此值称为B

By doing this, all the numbers present in the original array will get XORed to their counterparts and will evaluate to 0. The only number left in the end will be the missing number.

这样,原始数组中存在的所有数字都将与其对应的数字进行异或运算,并求值为0。最后剩下的唯一数字将是缺失的数字。

A ^ B = missing number

☝️?✌️? 数位?? ?? (☝️?✌️? Counting Bits ?? ??)

This is one of those problems where writing down the answer for various test cases and observing the results for patterns really helps. So, we will do exactly that and use the pattern we find to directly arrive at the final algorithm.

这是写下各种测试用例答案并观察模式结果的确有帮助的问题之一。 因此,我们将精确地做到这一点,并使用发现的模式直接得出最终算法。

Let’s look at the number of 1’s in the binary representation of the first 16 numbers.

让我们看一下前16个数字的二进制表示形式中的1。

0  1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  160  1  1  2  1  2  2  3  1  2   2   3   2   3   3   4   1

The pattern being formed above is the following:

上面形成的图案如下:

An even number, E, has the same number of bits as that of E / 2

偶数E具有与E / 2相同的位数

That’s all there is to see in the results above. This is the algorithm in its entirety. All we have to do is to iterate on numbers from 0..n and use the two rules above and we solved the problem like a boss ?.

以上就是所有要查看的结果。 这是整个算法。 我们要做的就是迭代从0..n开始的数字,并使用上面的两个规则,我们像老板一样解决了这个问题。

If you paid attention to the core idea of dynamic programming discussed during the beginning of the article, you’d know that this here, in essence, is a dynamic programming problem.

如果您关注本文开头所讨论的动态编程的核心思想,那么您将实质上知道这是一个动态编程问题。

We make use of the result for a previous subproblem (smaller number) to calculate the answer (number of set bits) for the current subproblem.

我们利用前一个子问题(较小的数字)的结果来计算当前子问题的答案(设置位数)。

On the face of it, we don’t need any bit manipulation as such to solve this problem. It can be solved by simple if-else clauses and a for loop.

从表面上看,我们不需要任何操作即可解决此问题。 可以通过简单的if-else子句和for循环来解决。

So, this is not really a bitmasking + dynamic programming kind of problem.

因此,这并不是真正的位掩蔽+动态编程问题。

Let’s look at a simple solution based on the ideas above.

让我们来看一个基于上述想法的简单解决方案。

This is a perfectly good way to solve this problem. For every index, i, we check the number of bits in the number i / 2 and also add 1 if the current number is odd.

这是解决此问题的绝佳方法。 对于每个索引i ,我们检查数字i / 2的位数,如果当前数字为奇数,则还要加1

We can, however, solve it in a geekier way, so to say using bit manipulation. The two operations being performed are:

但是,我们可以用怪异的方式解决它,也就是说可以使用位操作。 正在执行的两个操作是:

  1. division by 2 and除以2和
  2. checking if the number is even or not.检查数字是否为偶数。

We have already seen the use of the right shift operator, &gt;>, for division by 2.

我们已经看到使用右移运算符&g t;>进行2除。

As for the second operation, we can simply check if the least significant is set or not. If you notice, all the odd numbers have their least significant bit set. While the even numbers don’t.

至于第二个操作,我们可以简单地检查是否设置了最低有效位。 如果您注意到,所有奇数都将设置其最低有效位。 而偶数则没有。

We have already seen how to check if a particular bit is set or not using the AND operator. Let’s see a geekier ? version of the above code.

我们已经看到了如何使用AND运算符检查如何设置特定位。 让我们看看怪异的人吗? 以上代码的版本。

If you look at the runtimes for both the programs, they’re almost the same. A major chunk of the time is consumed by the construction of the output array.

如果您查看两个程序的运行时,它们几乎是相同的。 输出数组的构造消耗了大部分时间。

Bitwise operations are always optimized and are faster than other higher level programming constructs.

逐位运算总是经过优化的,并且比其他更高层次的编程结构要快。

Word字长的最大乘积⛩ (⛩ Maximum Product of Word Lengths ⛩)

There’s good news and bad news. The good news is that a slightly optimized version of the brute force algorithm will get your code accepted on the platform.

好消息和坏消息。 好消息是,蛮力算法的略微优化版本将使您的代码在平台上被接受。

The brute force way is to check all the pairs of words and for each pair, check if there are any common characters between them. For all such pairs that don’t have any common characters, record the maximum value of len(word1) * len(word2).

暴力破解方法是检查所有单词对,并检查每对单词之间是否存在任何公共字符。 对于所有没有任何公共字符的此类对,记录len(word1) * len(word2)的最大值。

The bad news is that this algorithm is extremely slow. Let’s look at the percentage of solutions on LeetCode which this brute force algorithm is able to beat.

坏消息是该算法非常慢。 让我们看一下这种蛮力算法能够克服的LeetCode解决方案的百分比。

Shame on you, Sachin!

萨钦,真可惜!

That’s not the kind of stats you want to see for your submission. If you’re someone who simply wants to solve a problem, then your job here is done. Nothing more to do. But, if you’re like me and if you want to make this dog stop ?, then read on!

那不是您想查看的统计数据。 如果您只是想解决问题的人,那么您的工作已经完成。 无事可做。 但是,如果您像我一样,并且想让这条狗停下来,请继续阅读!

Let’s look at the time complexity for this brute force algorithm before moving onto a much more optimized solution using bit manipulation.

让我们先看一下这种蛮力算法的时间复杂度,然后再使用位处理来寻求更优化的解决方案。

We consider all possible pairs of words from the given array. Considering there are N words in the given array, we will get O(N²) complexity right off the bat.

我们考虑给定数组中所有可能的单词对。 考虑到给定数组中有N单词,我们马上就能获得O(N²)复杂度。

Other than that, we have a dictionary containing sets of characters for each of the words in the dictionary. Considering the size of the alphabet to be 26 (lowercase alphabets only), each set can potentially be of size 26.

除此之外,我们还有一个字典,其中包含字典中每个单词的字符集。 考虑到字母的大小为26(仅小写字母),每个集合的大小可能为26

For each pair of words, we perform a set intersection to check whether the two words have any common characters or not. Set intersection takes linear time and hence, the overall complexity for this algorithm would come out to be O(26N²) which is essentially O(N²).

对于每对单词,我们执行一个设置交集以检查两个单词是否具有任何公共字符。 集合相交花费了线性时间,因此,该算法的总体复杂度为O(26N²) ,基本上是O(N²)

It turns out that we can’t get rid of the part where we have to consider each pair of words from the given array. So, we can’t get rid of the O(N²) part of the algorithm.

事实证明,我们不能摆脱必须考虑给定数组中每对单词的部分。 因此,我们无法摆脱算法的O(N²)部分。

The portion that we can get rid of, however, is the part where we compare two words and see if they have any common characters. That constant 26 slows down the algorithm a lot.

但是,我们可以除去的部分是比较两个单词并查看它们是否具有任何公共字符的部分。 常数26会使算法大大减慢速度。

An important thing to note here is that the question simply cares about common characters and not their frequency or their order.

这里要注意的重要一点是,该问题只关心常见字符,而不是它们的频率或顺序。

What if we simply use a bitmask to represent the characters in a word?

如果我们仅使用位掩码来表示单词中的字符怎么办?

What we can do here is to have a bitmask consisting of 26 bits to represents the characters belonging to a particular word. Let’s look at such a representation for a few words to make things clearer.

我们在这里可以做的是拥有一个由26位组成的位掩码,以表示属于特定单词的字符。 让我们看一下这样的表述,以使情况更清楚一些。

Hope the above figure makes it clear what we mean by a bitmask representing a corresponding word. Once we have these bitmasks for all the words, all that remains now is to check if two words have any common characters or not.

希望上面的图可以清楚地表示代表相应单词的位掩码的含义。 一旦所有单词都具有这些位掩码,现在剩下的就是检查两个单词是否具有任何公共字符。

If two words would have any common characters, then the corresponding bits for those characters would be set in the bitmasks for both the words.

如果两个单词具有任何公共字符,则将在两个单词的位掩码中设置这些字符的相应位。

Hence, all we have to do is to do a bitwise AND of the bitmasks representing two words and check if we get a 0 or not. If we do end up getting a 0, that would imply no intersection and that is precisely what we are looking for.

因此,我们要做的就是对代表两个单词的位掩码进行按位AND运算AND并检查是否得到0。 如果最终得到0,则意味着没有交集,而这正是我们要寻找的。

For e.g. Let's consider two words "hello" and "jack"
The bitmask corresponding to "hello" will have the bits for 'h', 'e', 'l', and 'o' set.
The bitmask corresponding to "jack" will have the bits for 'j', 'a', 'c', and 'k' set.
Since these words don't have any character in common, the bitwise AND of their bitmasks will give a 0.
Had there been any common characters between them, then both their bitmasks would have set bits at the same indexes (corresponding to the common letters) and hence we would get a non-zero bitwise AND.

Let’s look at the code for this modification in the algorithm we just discussed.

让我们看看刚才讨论的算法中用于此修改的代码。

Bitwise operations are super quick. Let’s look at the performance of this algorithm on the LeetCode platform to corroborate the quickness of bit manipulation.

按位运算非常快。 让我们看看在LeetCode平台上该算法的性能,以证实位处理的快速性。

Not bad, right? We improved the runtime for our program from 1820 ms earlier to 712 ms now. That’s a huge improvement, isn’t it? It turns out, we can improve this algorithm even further.

还不错吧? 我们将程序的运行时间从之前的1820毫秒改进到现在的712毫秒 。 这是一个巨大的进步,不是吗? 事实证明,我们可以进一步改进该算法。

The improvement can be made because of the fact that two different words can have the same bitmask.

由于两个不同的字可以具有相同的位掩码,因此可以进行改进。

For e.g. hello and llohhel both would have the same bitmask. We can store the longest word for a given bitmask since all we care about is maximizing the product of word lengths.

例如, hellollohhel都将具有相同的位掩码。 我们可以为给定的位掩码存储最长的字,因为我们关心的是使字长的乘积最大化。

Let’s look at the code after incorporating this improvement.

让我们看看合并了此改进后的代码。

By doing this very optimization, the runtime comes down to 208ms ???

通过进行这种非常优化,运行时间可降至208ms?

使用位掩码设置表示 (Set Representation using Bitmasking)

We introduced an idea in the previous problem which will be crucial in the next two problems that we will discuss. Essentially, we used the idea of bitmasking to represent elements belonging to a set.

我们在上一个问题中引入了一个想法,这对于我们将讨论的下两个问题至关重要。 本质上,我们使用位掩码的思想来表示属于集合的元素。

In the previous article, we considered a set of 26 alphabets and we resented that by using a bitmask containing 26 bits. A 0 at a particular index in the mask would represent set exclusion whereas a 1 would represent set inclusion.

在上一篇文章中,我们考虑了一组26个字母,并使用包含26位的位掩码对此表示不满。 掩码中特定索引处的0表示集合排除,而1表示集合包含。

This idea is very crucial for dynamic programming problems that deal with subproblems involving a subset of numbers. We can’t really cache a subset of numbers. A set is not a hashable data structure.

这个想法对于处理涉及数字子集的子问题的动态编程问题至关重要。 我们真的不能缓存数字的子集。 集合不是可哈希的数据结构。

For e.g. suppose we have an array of numbers [4, 3, 6]Let's look at all possible subsets of this array.[][4][3][6][4, 3][4, 6][3, 6][4, 3, 6]For dynamic programming problems where intermediate states are defined by these subsets, we need a memory efficient way of performing caching.

For eg suppose we have an array of numbers [4, 3, 6] Let's look at all possible subsets of this array.[] [4] [3] [6] [4, 3] [4, 6] [3, 6] [4, 3, 6]For dynamic programming problems where intermediate states are defined by these subsets, we need a memory efficient way of performing caching.

What do we do in such a scenario?

在这种情况下我们该怎么办?

This is where we have bitmasks come in. They are memory-efficient ways of representing subsets of elements. Let’s have a look at how we can represent the subsets above using bitmasks.

这就是我们要使用位掩码的地方。它们是表示元素子集的内存有效方式。 让我们看一下如何使用位掩码表示上面的子集。

Since we have 3 elements in our given array, [4, 3, 6] we can make use a a 3-bit number for representing each of these elements. An empty subset would be represented by 000. Let's look at each of the subsets along with their bit representations.000 -->> []001 -->> [6]010 -->> [3]011 -->> [3, 6]100 -->> [4]101 -->> [4, 6]110 -->> [4, 3]111 -->> [4, 3, 6]

Since we have 3 elements in our given array, [4, 3, 6] we can make use aa 3-bit number for representing each of these elements. An empty subset would be represented by 000. Let's look at each of the subsets along with their bit representations.000 -->> [] Since we have 3 elements in our given array, [4, 3, 6] we can make use aa 3-bit number for representing each of these elements. An empty subset would be represented by 000. Let's look at each of the subsets along with their bit representations.000 -->> [] 001 -->> [6] 010 -->> [3] 011 -->> [3, 6] 100 -->> [4] 101 -->> [4, 6] 110 -->> [4, 3] 111 -->> [4, 3, 6]

Depending upon the problem’s constraints, we can make use of a bitmask. For e.g. in the previous problem, the alphabet set was limited to the size 26. A lot of programming problems that have small array sizes and involve dynamic programming that require you to hash subsets, usually are an indication of bitmasking-based approach.

根据问题的约束,我们可以使用位掩码。 例如,在先前的问题中,字母集被限制为大小26。许多编程问题具有较小的数组大小,并且涉及需要散列子集的动态编程,通常表明基于位掩码的方法。

A 26-bit mask, e.g. in the previous problem, is essentially an integer and we can simply cache that integer in a dictionary. This saves on the memory footprint of the algorithm and greatly cuts down on the time complexity for the algorithm since bit manipulation is very efficient. We can easily include and exclude elements from the subset by masking and unmasking corresponding bits from the mask.

一个26位的掩码,例如在前面的问题中,本质上是一个整数,我们可以简单地将该整数缓存在字典中。 由于位操作非常有效,因此节省了算法的内存空间并大大降低了算法的时间复杂度。 通过从掩码中屏蔽和取消屏蔽相应的位,我们可以轻松地从子集中包含和排除元素。

Let’s look at another problem based on this idea before we finally discuss the star problem of this article.

在最终讨论本文的关键问题之前,让我们看一下基于此思想的另一个问题。

分区数组为K个相等和子集 (??Partition Array to K Equal Sum Subsets ??)

This is just one of those questions that begs you to use bitmasking. Notice the size of the array. It’s just 16 elements. If we consider a bitmask to represent the elements in an array, we’d have a 16-bit integer at our hands.

这只是乞求您使用位掩码的那些问题之一。 注意数组的大小。 只有16个元素。 如果我们考虑使用位掩码来表示数组中的元素,那么我们将拥有一个16位整数。

That means, to represent all the possible subsets of a given array, we’d have 2¹⁶ possible integers. We don’t really need actual subsets. All we need is a bitmask telling us the elements belonging to that subset. Based on this idea, let’s look at dynamic programming-based approach to solve the above problem.

这意味着,要表示给定数组的所有可能子集,我们将有2 1/1可能的整数。 我们实际上并不需要实际的子集。 我们所需要的只是一个位掩码,告诉我们属于该子集的元素。 基于此想法,让我们看一下基于动态编程的方法来解决上述问题。

The problem statement only asks us if a k-equal-sum partitioning is possible or not. It doesn’t ask us to return the actual partitioning. That makes the problem a whole lot simpler.

问题陈述仅询问我们是否可以进行k个等和分区。 它不要求我们返回实际的分区。 这使问题变得更加简单。

We don’t care which partition an element belongs to as long as the overall sum of a partition is what total_sum / k.

只要一个分区的总和是total_sum / k我们就不在乎元素属于哪个分区。

As we’ve mentioned in the above paragraphs, we will be using a bitmask to represent the elements of the array.

如前所述,我们将使用位掩码来表示数组的元素。

We will use the individual bit states of the mask to identify which numbers have already been assigned a partition and which ones still remain to be assigned one. We can only assign a number to a partition if after adding it to that partition, the total sum remains <= total_sum / k .

我们将使用掩码的各个位状态来确定哪些数字已经分配了一个分区,哪些数字仍然分配了一个分区。 仅当将一个数字添加到该分区后,总和仍为<= total_sum / k时,我们才能为其分配一个数字。

If the sum, S, of all the elements of the array is divisible by k, we have to complete k — 1 partitions since the last partition will automatically fall into place. In case the total sum is not divisible by k , then such an equal sum partitioning is not possible.

如果数组的所有元素的和Sk整除,则我们必须完成k — 1分区,因为最后一个分区将自动归位。 如果总和不能被k整除,则这样的等和划分是不可能的。

为什么要动态编程? (Why dynamic programming?)

Before looking at the code for this problem based on the ideas we’ve discussed above, it’s important to understand how dynamic programming fits into the picture. We’ve already seen why bitmasking would come in handy. But where exactly does dynamic programming fit in?

在根据我们上面讨论的思想来研究该问题的代码之前,重要的是要了解动态编程是如何适应画面的。 我们已经知道为什么位屏蔽会派上用场了。 但是动态编程到底适合哪里呢?

Recursion is a natural fit for the problem since we have got a set of N elements and we need to partition them into k different subsets all of which have an equal sum.

递归很自然地适合该问题,因为我们有一组N元素,我们需要将它们划分为k不同的子集,它们的总和相等。

Since we don’t really know what partition a number should belong to, we have to try out all the options. That means, for a given partition, we try adding all available numbers recursively (within the constraints of the partition sum) and see which choice leads us to an answer.

由于我们实际上并不知道数字应该属于哪个分区,因此我们必须尝试所有选项。 这意味着,对于给定的分区,我们尝试递归地添加所有可用数字(在分区总和的约束范围内),然后看看哪个选择会导致我们得出答案。

The recursion would be based on the following three variables:

递归将基于以下三个变量:

  1. the number of partitions remaining.

    剩余number of partitions

  2. the mask representing what elements in the original array are unassigned.

    代表原始数组中哪些元素的未分配 mask

  3. and the current partition sum.

    和当前partition sum

As we all know, for any problem to be classified as a dynamic programming problem, it should have the following two properties:

众所周知,对于要归类为动态编程问题的任何问题,它都应具有以下两个属性:

  1. Optimal sub-structure ~ which simply means that the original problem must be breakable into subproblems and optimal solutions to the subproblems should be usable in a way to find the optimal solution to the main problem.

    最佳子结构 〜仅仅意味着原始问题必须可分解为子问题,并且该子问题的最优解应以找到主要问题的最优解的方式使用。

  2. Overlapping subproblems ~ which mean there are multiple recursive calls to the same subproblems and to avoid repeated computations, we can cache the results for our subproblems.

    子问题重叠 〜表示对同一子问题有多个递归调用,并且为了避免重复计算,我们可以为子问题缓存结果。

Let’s look at the recursion tree for this problem to understand if we have any overlapping subproblems. We already have the first property satisfied.

让我们看一下该问题的递归树,以了解我们是否有任何重叠的子问题。 我们已经满足了第一个属性。

In the above recursion tree, we consider K = 2. That means we need 2 partitions each of sum 18. Since we never reach 18 in the tree above, the K never gets reduced. This is just a part of the recursion tree and the complete version.

在上面的递归树中,我们考虑K =2。这意味着我们需要2个分区,每个分区的总和为18 。 由于我们从未在上面的树中达到18 ,因此K永远不会减少。 这只是递归树和完整版本的一部分。

We can clearly see two recursion states getting repeated. We get the same mask 0011 twice. We can simply store the result once and then re-use it later on.

我们可以清楚地看到两个递归状态被重复。 我们两次获得相同的面罩0011 我们只需将结果存储一次,然后再使用即可。

Now let’s look at the code for based on bitmasking + dynamic programming.

现在,让我们看一下基于位掩码+动态编程的代码。

Now we are finally ready to look at the main problem of this article. The problem that is not as straightforward in its bitmasking application as the current one and the previous one.

现在,我们终于准备好研究本文的主要问题。 在其位掩码应用程序中,这个问题并不像当前版本和上一个版本那样简单明了。

查找最短的超级字符串? (? Find the Shortest Superstring ?)

What do you think the simplest way of forming a superstring is?

您认为形成超弦的最简单方法是什么?

You simply take any permutation of the given words and all of these permutations will be valid superstrings.

您只需对给定单词进行任何排列,所有这些排列都是有效的超字符串。

However, the question doesn’t ask us to form any superstring. It asks us to form the shortest superstring covering all the words.

但是,该问题并不要求我们形成任何超弦。 它要求我们形成覆盖所有单词的最短超串。

For this problem, we will explore the idea of chaining words together. Intuitively, you can consider the idea of chaining as set union.

对于这个问题,我们将探讨将单词链接在一起的想法。 直观地,您可以考虑将链接作为集合联合的想法。

Let’s consider two different set of elements and then look at the combined set containing all their elements together i.e. the union set.

让我们考虑两个不同的元素集,然后查看包含所有元素在一起的组合集,即联合集。

As we can see above, whenever we do a union over two sets, the common elements only appear once. We will adopt a similar idea with chaining two words together.

正如我们在上面看到的,每当我们对两个集合进行并集时,公共元素只会出现一次。 我们将采用类似的思路,将两个单词链接在一起。

Essentially, when chaining words A and B together, we will only consider the common portion between the suffix of A and prefix of B exactly once. Let’s look at the diagrammatic representation for chaining of two words.

本质上,当将单词AB链接在一起时,我们仅考虑一次A后缀和B前缀之间的公共部分 让我们看一下两个单词的链接的图解表示。

This is an important concept that will come in handy for solving this problem. So as to avoid repeated computations of finding out the common portion between two words which are being chained together, we will do some preprocessing.

这是一个重要的概念,对于解决此问题非常有用。 为了避免重复计算以找出链接在一起的两个单词之间共同部分,我们将进行一些预处理。

We will find out the length of the suffix of A that overlaps with the prefix of B where A and B are being chained together. We will do this for all the pairs of words in our given list. In Python, given a string S, we can make use of S[i:] to obtain all the suffixes and S[:i] to obtain all the prefixes. We make use of this for our preprocessing below.

我们将找出A的后缀的长度,该后缀的长度与B的前缀重叠,其中A and B被链接在一起。 我们将对给定列表中的所有单词对执行此操作。 在Python中,给定字符串S ,我们可以使用S[i:]获得所有后缀,并使用S[:i]获得所有前缀。 我们将其用于下面的预处理。

Now that the idea of chaining is clear, we can move onto the next part of the problem where we explain why this problem can be solved recursively.

既然链接的概念很明确,我们就可以进入问题的下一部分,在其中解释为什么可以递归解决此问题。

为什么要递归? (Why Recursion?)

Now that we are familiar with the concept of chaining, we know that whenever we chain two words together, the new word formed can be shorter than the combined lengths of the two original words.

现在我们已经熟悉了链接的概念,我们知道,只要将两个单词链接在一起,形成的新单词就会比两个原始单词的组合长度短。

Extending this idea to all the words in the given list, we have to form a superstring that will be formed by chaining one word after the other until we are done with all the words.

将此思想扩展到给定列表中的所有单词时,我们必须形成一个超串,该超串将通过将一个单词串接另一个单词形成,直到完成所有单词为止。

The important question here is, in what order should the words be chained together?

这里的重要问题是,单词应以什么顺序链接在一起?

For the three words aabc , hjaa, and chuj the chaining order hjaa → aabc → chuj = hjaabchuj is better than aabc → hjaa → chuj = aabchjaachuj since the former gives a superstring of length 9 as opposed to 12 in the latter order.

对于三个单词aabchjaachuj ,链接顺序hjaa → aabc → chuj = hjaabchujaabc → hjaa → chuj = aabchjaachuj因为前者给出的长度为9的超aabc → hjaa → chuj = aabchjaachuj ,而后者给出的长度为12。

So, the chaining order determines the overall length of the formed superstring.

因此,链接顺序决定了所形成的超弦的总长度。

We will be trying out all possible arrangements for the superstrings using the given list of words and choosing the shortest one.

我们将使用给定的单词列表并选择最短的单词, 尝试所有可能的超字符串安排

这似乎合法,但是为什么要动态编程呢? (That seems legit, but why dynamic programming? ?)

Instead of relying on a recursion tree for explaining the need for dynamic programming, we will look at an example that will explain the same idea.

而不是依靠递归树来解释对动态编程的需求,我们将看一个示例来解释相同的想法。

Suppose we are given a set of 5 words [A, B, C, D, E] and we are to form the shortest superstring that contains all the words.

假设给定一组5个单词[A, B, C, D, E] ,我们将形成包含所有单词的最短超字符串。

We already know that we are solving this problem recursively. Suppose in the middle of our recursion, we are already done deciding the chaining order of the first 3 words. Let’s say this ordering was B → A → C . Now, we need to find out the best way to chain D and E after C so that the overall superstring length is minimized.

我们已经知道我们正在递归解决此问题。 假设在递归过程中,我们已经确定了前三个单词的链接顺序。 假设此排序为B → A → C 现在,我们需要找到在C之后链接D和E的最佳方法,以使总的超弦长度最小化。

Let’s say that in our recursion we found that C → E → D was the best chaining order. We don’t know yet if the superstring formed via B → A → C → E → D is the shortest one. However, we know that C → E → D is the best chaining order for the three words C, D, and E .

假设在递归中我们发现C → E → D是最好的链接顺序。 我们还不知道通过B → A → C → E → D形成的超弦是否最短。 但是,我们知道C → E → D是三个单词C, D, and E的最佳链接顺序。

Suppose now we arranged the first three words a little differently in our recursion (a separate recursion path) and now we have A → B → C and we have to recurse again to find out the best possible arrangement for E and D . However, we already know from above the best possible arrangement is C → E → D . Why calculate this again? This is where dynamic programming comes into the picture.

假设现在我们在递归中(单独的递归路径)对前三个单词的排列稍有不同,现在我们有A → B → C ,我们必须再次递归以找出E and D的最佳排列方式。 但是,我们已经从上面知道最好的安排是C → E → D 为什么要再次计算? 这是动态编程出现的地方。

That’s it? Go on. Explain some more.

而已? 继续。 解释更多。

Nah, let’s now look at the the Python code bringing all these ideas together.

不,现在让我们看一下将所有这些想法融合在一起的Python代码。

Uh oh! Where the heck is bitmasking in all this?

哦哦! 这一切到底在哪里掩盖?

好吧,让我们开始屏蔽一下吗? (Ok ok, let’s get to bitmasking ?)

Let’s rephrase the problem in a slightly different manner that will make the requirement for bitmasking very clear.

让我们以稍有不同的方式重新描述问题,这将使位屏蔽的要求非常明确。

Given a set of N elements, we need to find a suitable arrangement for them that will minimize a certain metric. Since we are relying on dynamic programming, we will be caching results for subproblems.

给定一组N元素,我们需要为它们找到一个合适的安排,以最小化某个指标。 由于我们依赖于动态编程,因此我们将为子问题缓存结果

Our subproblems will be represented by the subset of these N items. As we’ve seen in this article and especially in the previous two problems, it’s difficult to cache subsets as it is.

我们的子问题将由这N个项目的子集表示。 正如我们在本文中特别是在前面两个问题中所看到的那样,很难按原样缓存子集。

In case the problem constraints are small, we can represent the elements of the list using a bitmask and use that for caching purposes.

如果问题约束很小,我们可以使用位掩码表示列表中的元素,并将其用于缓存。

After all, a bitmask is just a number.

毕竟,位掩码只是一个数字。

Let’s finally get to the code for finding the shortest superstring given a set of N words.

最后,让我们看一下在给定N个单词的情况下找到最短超字符串的代码。

  • Line 3 ~ The initial mask is composed of all 1s. That implies all the words are available for chaining. If the mask becomes 0, that means no word is left for chaining. So, the length of the superstring would be 0. This is the base case.

    第3行〜初始掩码由全1组成。 这意味着所有单词都可用于链接。 如果掩码变为0,则表示没有字可链接。 因此,超字符串的长度将为0。这是基本情况。

  • Line 7 ~ If you’ve paid attention to the example involving 5 words [A, B, C, D, E] from before, you know that the opportunity of caching arises when we have the two words D and E to be chained together following C.

    第7行〜如果您从前面注意到了涉及5个单词[A, B, C, D, E]的示例,您就会知道,当我们将两个单词D and E链接在一起时,就会出现缓存的机会跟随C.

    Line 7 ~ If you’ve paid attention to the example involving 5 words [A, B, C, D, E] from before, you know that the opportunity of caching arises when we have the two words D and E to be chained together following C. Thus, we need to know the previous word in the superstring so far so as to attach the rest of the words. Hence, prev_i, which represents the index in the original array of the previous word in the superstring is also used for caching purposes apart from the mask.

    第7行〜如果您从前面注意到了涉及5个单词[A, B, C, D, E]的示例,您就会知道,当我们将两个单词D and E链接在一起时,就会出现缓存的机会跟随C. 因此,到目前为止,我们需要知道超字符串中的前一个单词,以便附加其余单词。 因此, prev_i ,代表prev_i前一个单词的原始数组中索引的prev_i也用于缓存目的。

  • Line 12, 15 ~ We always check all the given set of words and only consider those as options for the current step in our recursion which have not been previously used. We make use of bitmasking for this. To see if a word has been used or not, we simply check if the bit at the corresponding index in the mask is unset or not.

    第12、15行〜我们总是检查所有给定的单词集,仅将它们视为递归中当前步骤的选项,这些选项以前没有使用过。 为此,我们使用位屏蔽。 要查看是否已使用一个单词,我们只需检查掩码中相应索引处的位是否未设置

  • Line 18 ~ Makes use of the preprocessing we did earlier. For the word to be chained / attached to the word at index prev_i , we know the amount of overlap between the two. Thus, if we decide on considering the word at index i as the next word in our recursion, the amount of length it will add to our superstring would be len(A[i]) — overlap between the words A[prev_i] and A[i]

    第18行〜利用我们之前所做的预处理。 对于要在索引prev_i处链接/附加到单词的单词,我们知道两者之间的重叠量。 因此,如果我们决定将索引i上的单词视为递归中的下一个单词,则它将添加到超len(A[i]) — overlap between the words A[prev_i] and A[i]的长度为len(A[i]) — overlap between the words A[prev_i] and A[i]

All this is fine and dandy, but wasn’t the original question asking for the superstring itself and not just the length of the shortest superstring?

所有这一切都很好,但不是原来的问题是要超级字符串本身,而不仅仅是最短超级字符串的长度吗?

Am I shying away from solving the entire problem? Of course not!

我要回避解决整个问题吗? 当然不是!

The function recurse simply returns the length of the shortest superstring and not the superstring itself. The problem statement, however, asks us to find out the shortest superstring.

函数recurse仅返回最短超recurse长度 ,而不是超字符串本身的长度 然而,问题陈述要求我们找出最短的超弦。

If you look at the code carefully, you’ll notice a parent dictionary. At every step in our recursion, we have multiple options of words which can be chained to the current superstring (thus extending it). We use the parent dictionary to store the word at each step that gave the best answer (shortest length).

如果仔细看一下代码,您会注意到parent字典。 在递归的每个步骤中,我们都有多个单词选项,可以将这些单词链接到当前的超字符串(从而扩展它)。 我们使用父字典在给出最佳答案(最短长度)的每个步骤中存储单词。

For a given mask — which tells us which words have already been chained together — and a given previous word (prev_i), the parent dictionary stores the next word that in the superstring that gives the optimal answer.

对于给定的掩码(它告诉我们哪些单词已经链接在一起)和给定的上一个单词( prev_i ), parent词典将下一个单词存储在超prev_i ,以给出最佳答案。

We will use this dictionary to backtrack and form the shortest superstring.

我们将使用此字典回溯并形成最短的超字符串。

In the end, that’s one of the best sights for a competitive programmer, especially if it’s for a hard problem in a timed competition.

In the end, that's one of the best sights for a competitive programmer, especially if it's for a hard problem in a timed competition.

Conclusion ?? (Conclusion ??)

  • Operations on bits are highly efficient and they are generally performed in parallel via optimized system level instructions.

    Operations on bits are highly efficient and they are generally performed in parallel via optimized system level instructions.

  • It’s difficult, but not impossible, to write clean, understandable code involving bit manipulations.

    It's difficult, but not impossible, to write clean, understandable code involving bit manipulations.

  • AND , OR , and NOT are the three fundamental bitwise operators.

    AND , OR , and NOT are the three fundamental bitwise operators.

  • The left-shift &lt;< and the right-shift >> operators are used for multiplication or division by 2.

    The left-shift &l t;< an d the right-sh ift >> operators are used for multiplication or division by 2.

  • The XOR operator is a versatile operator that can be used in many different programming problems. It returns True only when both the bits are opposites and False otherwise.

    The XOR operator is a versatile operator that can be used in many different programming problems. It returns True only when both the bits are opposites and False otherwise.

  • Dynamic Programming-based solutions involve some sort of caching for the results of subproblems. The subproblems are represented by a set of variables. These variables are not necessarily primitive data types.

    Dynamic Programming-based solutions involve some sort of caching for the results of subproblems. The subproblems are represented by a set of variables. These variables are not necessarily primitive data types.

  • Bitmasking comes in very handy in dynamic programming problems when we have to deal with subsets and the list/array size is small. A mask (having all 0s or all 1s) can represent the elements of the set and setting or unsetting a bit can mean inclusion and exclusion from the set.

    Bitmasking comes in very handy in dynamic programming problems when we have to deal with subsets and the list/array size is small. A mask (having all 0s or all 1s) can represent the elements of the set and setting or unsetting a bit can mean inclusion and exclusion from the set.

  • If the array/list/set size is, say, around 20, then you’d have 2²⁰ possible bitmasks which comes out to be almost a million of them. It’s not always the case you will encounter all of these bitmasks in a test case. However, that’s the maximum possible number.

    If the array/list/set size is, say, around 20, then you'd have 2²⁰ possible bitmasks which comes out to be almost a million of them. It's not always the case you will encounter all of these bitmasks in a test case. However, that's the maximum possible number.

  • We have to be careful about when to use bitmasking. We can’t have a 100-bit mask as that would be computationally intractable.

    We have to be careful about when to use bitmasking. We can't have a 100-bit mask as that would be computationally intractable.

  • For a solution to be eligible for dynamic programming, it should satisfy the optimal substructure and the overlapping subproblems properties. Usually, through some practice, you can start to identify if the problem at hand will have a DP based solution or not.

    For a solution to be eligible for dynamic programming, it should satisfy the optimal substructure and the overlapping subproblems properties. Usually, through some practice, you can start to identify if the problem at hand will have a DP based solution or not.

References ? (References ?)

  • https://www.hackerearth.com/practice/notes/bit-manipulation/

    https://www.hackerearth.com/practice/notes/bit-manipulation/

  • https://blog.bitsrc.io/bitmask-there-is-space-at-the-bottom-5a741d18c4e3

    https://blog.bitsrc.io/bitmask-there-is-space-at-the-bottom-5a741d18c4e3

  • https://bit.ly/2QRTIpj

    https://bit.ly/2QRTIpj

  • https://bit.ly/2CCUCxn

    https://bit.ly/2CCUCxn

  • https://www.geeksforgeeks.org/bitmasking-and-dynamic-programming-set-1-count-ways-to-assign-unique-cap-to-every-person/

    https://www.geeksforgeeks.org/bitmasking-and-dynamic-programming-set-1-count-ways-to-assign-unique-cap-to-every-person/

  • https://www.geeksforgeeks.org/bitmasking-dynamic-programming-set-2-tsp/

    https://www.geeksforgeeks.org/bitmasking-dynamic-programming-set-2-tsp/

  • https://codeforces.com/blog/entry/17973

    https://codeforces.com/blog/entry/17973

It’s been a long article and one that I’ve absolutely loved writing. If you’ve read this far, then you’ve probably found this article really helpful. Do spread some love by sharing it as much as possible and destroy that clap button! ?

It's been a long article and one that I've absolutely loved writing. If you've read this far, then you've probably found this article really helpful. Do spread some love by sharing it as much as possible and destroy that clap button!

Oh, if you’d like to read more such awesome articles all lit up with animations and beautiful, explanatory images, do checkout our newly opened kitchen.

Oh, if you'd like to read more such awesome articles all lit up with animations and beautiful, explanatory images, do checkout our newly opened kitchen .

Believe me, we’ve got some lip smacking recipes for you and with the holiday season upon us, I’m sure you’ll love them.

Believe me, we've got some lip smacking recipes for you and with the holiday season upon us, I'm sure you'll love them.

Wishing you all a Merry Christmas and a Happy New Year!

Wishing you all a Merry Christmas and a Happy New Year!

翻译自: https://www.freecodecamp.org/news/unmasking-bitmasked-dynamic-programming-25669312b77b/

如何在编程时屏蔽输入法

如何在编程时屏蔽输入法_取消屏蔽位屏蔽的动态编程相关推荐

  1. 出现字迹模糊迹象_改变迹象:如何使用动态编程解决竞争性编程问题

    出现字迹模糊迹象 by Sachin Malhotra 由Sachin Malhotra 改变迹象:如何使用动态编程解决竞争性编程问题 (Change the signs: how to use dy ...

  2. sis防屏蔽程序_智能化弱电工程屏蔽机房基础知识

    前言: 屏蔽机房基本原理来自法拉第笼设计.在没有做屏蔽的情况下,我们的电子设备会受到直击雷或间接雷等强电磁干扰源的影响导致设备无法工作或工作出现异常,最严重时出现损坏,这是比较常见的电磁干扰显现,另外 ...

  3. 动态规划编程面试_面试的前25大动态编程问题

    动态规划编程面试 Dynamic programming is one of the most asked paradigms in any product-based company intervi ...

  4. 编程时拼音输入法的设置

    注: 搜狗输入法的常用的快捷键 Shift : 中英文的切换 ctrl + shift + f : 简/繁体 的切换 不去探究写代码,究竟是五笔好还是拼音好,根据我的观察大部分岁数相对年轻的编程学习者 ...

  5. Java屏蔽输入法_输入框禁止输入法

    禁止输入法 形式: 参数说明形式: active 代表输入法为中文 inactive 代表输入法为英文 auto 代表打开输入法 (默认) disable 代表关闭输入法 具体例子如下: 禁止inpu ...

  6. Java屏蔽输入法_技巧:如何禁止输入法切换到全角状态

    如果你不愿意看解决过程,答案就是:注册热键.以下废话可以略过. 不知道大家有没有和我一样的烦恼.我在Delphi中编程的时候,由于需要使用Alt+Shift+Space来查看函数的声明,但是输入法的全 ...

  7. python网络编程要学吗_总算发现如何学习python网络编程

    为了提高模块加载的速度,每个模块都会在__pycache__文件夹中放置该模块的预编译模块,命名为module.version.pyc,version是模块的预编译版本编码,一般都包含Python的版 ...

  8. 数控g71编程实例带图_数控车G71有凹槽复合循环编程实例

    <数控车G71有凹槽复合循环编程实例>由会员分享,可在线阅读,更多相关<数控车G71有凹槽复合循环编程实例(3页珍藏版)>请在人人文库网上搜索. 1.数控车G71有凹槽复合循环 ...

  9. linux shell命令行及脚本编程实例详解_超全整理!这些Shell编程必备知识你都掌握了吗?...

    正文最近很多粉丝咨询我,被问到了一些Shell编程的问题,看看大家能否答出来: 1.shell脚本千千万,不知道从哪入手 2.没经验缺方法,面试通不过.做事没头绪 3.野路子.没人教自动化,做了几年基 ...

最新文章

  1. 08CSS的复合选择器
  2. 关于网络连接方式的总结(HostOnly,NAT....)
  3. python可以用来编写计算机网络程序吗_不必熟悉python或R编程语言,6步执行计算机视觉应用程序...
  4. 喜马拉雅 xm文件转m4a_4K YouTube to MP3 Mac(YouTube转mp3软件)
  5. C++实现链式基数排序
  6. php 自定义行间距,phpstorm 常见设置
  7. @程序员 天冷了,你准备抱键盘还是抱女朋友取暖?
  8. oracle脚本审核平台,Oracle 脚本(适应各种业务需要)
  9. ADMT3.2域迁移之Server2003至Server2012系列(五)添加域管理员权限
  10. html view设置全屏,uni-app 如何设置web-view 不全屏,不自动铺满,动态控制web-view的高度...
  11. 关于直播,所有的技术细节都在这里了(四)
  12. 除了赚钱,腾讯游戏还有诗和远方!
  13. TestCenter测试管理工具问题详解(3)
  14. Android之Scroller详解讲解-真正了解滚动处理
  15. 【ENOVIA 服务包】知识重用解决方案 | 达索系统百世慧®
  16. VISIO取消箭头自动吸附及粘连
  17. 前端常用素材网站大全
  18. 无刷直流电机的simulink仿真
  19. zip直链生成网站_调优-WEB资讯专栏-DMOZ中文网站分类目录
  20. SSL证书怎么购买?

热门文章

  1. echarts在没有数据时显示暂无数据
  2. 来微软人工智能大会,体验你触手可及的AI
  3. 隐藏NavigationBar
  4. 苹果会成为下一个诺基亚吗?
  5. XML解析---DOM解析和SAX解析
  6. 运行在笔记本上的OpenShift4环境
  7. QQ云输入法技术分析
  8. 聚焦用户需求,英韧推出企业级SATA主控及解决方案
  9. Sui Builder House首尔站倒计时!
  10. html如果更改背景音乐,如何替换视频背景音乐(替换视频中的音频),这个方法超简单!...