使用动态编程在给定字符串中计数组合
我在“算法和数据结构” 书中找到了一个练习,我无法解决。
从字符表开始:
| left | center |
|:---- |:-------|
| A | 0 |
| B | 00 |
| C | 001 |
| D | 010 |
| E | 0010 |
| F | 0100 |
| G | 0110 |
| H | 0001 |
这意味着从给定的字符串s = 00100
开始,有5
可能被解码的序列:ada ,
af
,caa
,cb
或ea
。
显然不是所有字符串都可以解码(例如
1111
)。
编写一个程序(在Java中),该程序可以计算可以通过 s 通过动态编程来编码 s 的序列数量。
另一个示例:字符串00010010001001001001001001001001100
具有5567
可能的序列。
提示: 子问题是 prefixes
s
我的尝试:
/**
* this is the main part of the algorithm. I feel like this is way too slow
* and it doesn't use dynamic programming.
* 'encodings' is just an ArrayList with the specified values (e.g 00).
*/
private void count(String str) {
for (String c : encodings) {
if (str.length() > c.length()) {
String subStringCodLength = str.substring(0, c.length());
if (subStringCodLength.equals(c)) {
String substring = str.substring(c.length());
count(substring);
}
} else if (str.length() == c.length()) {
if (encodings.contains(str)) {
this.numOfDecodings++;
break;
}
}
}
}
I found an exercise in the "Algorithms and Data Structures" book that I'm unable to solve.
Starting with a table of characters encoding:
| left | center |
|:---- |:-------|
| A | 0 |
| B | 00 |
| C | 001 |
| D | 010 |
| E | 0010 |
| F | 0100 |
| G | 0110 |
| H | 0001 |
This means that starting by a given string S = 00100
, there are 5
possible sequences that can be decoded: ADA
, AF
, CAA
, CB
or EA
.
Obviously not all strings can be decoded (e.g.
1111
).
Write a program (in Java) that calculates the number of sequences that can encode S through dynamic programming.
Another example: the string 000100100010010000100100001001100
has 5567
possible sequences.
Hint: the subproblems are the prefixes of
S
My attempt:
/**
* this is the main part of the algorithm. I feel like this is way too slow
* and it doesn't use dynamic programming.
* 'encodings' is just an ArrayList with the specified values (e.g 00).
*/
private void count(String str) {
for (String c : encodings) {
if (str.length() > c.length()) {
String subStringCodLength = str.substring(0, c.length());
if (subStringCodLength.equals(c)) {
String substring = str.substring(c.length());
count(substring);
}
} else if (str.length() == c.length()) {
if (encodings.contains(str)) {
this.numOfDecodings++;
break;
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
动态编程中有两种方法: remoization 和制表。
两者都是基于子问题的先前计算的结果的结果。正如提示所述,根据给定字符串的前缀将问题分为子问题。
Memoization
Memoization 技术与递归一起使用。存储中间结果的平均值的一个很好的选择是
MAP
。实施递归时,我们需要描述两种情况:
基本情况 - 代表一个简单的边缘案例(或一组边缘案例)预先知道结果。对于这个问题,此类边缘案例为:
1
(一个空的二进制字符串“”
结果在一个空字符串字符串“”“
”中),0
(在下面的解决方案中,当执行递归案例时自然而然地解决),递归案例 - 解决方案的一部分,递归调用是制成的,而主要逻辑居住的时候。在递归情况下,我们需要在字符串的开头找到每个二进制“二进制字母” ,然后通过传递子字符串递归调用该方法(没有“字母” )作为参数。这些递归调用的结果需要累积,存储在地图中,然后作为返回值提供。
它的外观是:
制表
制表技术不是基于递归,而是利用循环。
这使得这种方法更加可靠,因为递归有一定的限制,尤其是在Java中。制表允许处理一个可以使用 Memoization 产生
StackoverFlowerRor
的大量输入。通常,阵列用于在实现制表时存储中间结果。
要解决此问题,我们需要使用给定字符串 +
1
的长度创建一个 array 。数组的每个元素将代表使用一组“二进制字母” 的索引0
从索引0
收缩的方法数量。最终结果将存储在数组的最后一个位置。要初始化 array ,我们需要将
1
的值设置为对应于 “二进制字母” 的每个元素给定字符串的前缀。它是在pupulate()
方法中完成的。然后,我们需要根据已经找到的组合来迭代数组搜索可以联系的可能组合(即基于不是0
的数组元素)。demo
main()
输出:
a链接到在线演示
There are two approaches in Dynamic programming: Memoization and Tabulation.
Both are based and storing and reusing previously calculated results of subproblems. And as the hint says, divide the problem into subproblems based on the prefixes of the given string.
Memoization
Memoization technic is used together with recursion. A good choice of a mean for storing the intermediate results would be a
Map
.When implementing a recursion, we need to describe two situations:
Base case - that represents a simple edge-case (or a set of edge-cases) for which the outcome is known in advance. For this problem, such edge-cases are:
1
(an empty binary string""
results into an empty string of letters""
),0
(in the solution below it resolves naturally when the recursive case is being executed),Recursive case - a part of a solution where recursive calls a made and when the main logic resides. In the recursive case, we need to find each binary "binary letter" at the beginning of the string and then call the method recursively by passing the substring (without the "letter") as an argument. Results of these recursive calls need to be accumulated, stored in the map, and then provided as a return value.
That how it might look like:
Tabulation
Tabulation technic isn't based on recursion, instead it utilizes loops.
Which makes this approach more reliable, because recursion has some limitation, especially in Java. Tabulation allows processing a massive input that can produce
StackOverflowError
with Memoization.Usually, arrays are used to store intermediate results while implementing a Tabulation.
To solve this problem, we need to create an array with the length of the given string +
1
. Each element of the array will represent the number of ways to contract the substring from index0
up to the index of the current element using a set of "binary letters". The final result will be stored at the last position of the array.To initialize the array, we need to set a value of
1
to each element of the array that corresponds "binary letter" which happens to be a prefix of the given string. It's done in thepopulate()
method. Then we need to iterate over array searching for the possible combinations that can be contacted based on the combinations that has been already found (i.e. based on the array elements that are not0
).Demo
main()
Output:
A link to Online Demo