我正在解决这个问题 -
给定一个由a,b和c组成的字符串,我们可以选择任意两个相邻的字符串 不同的字符,并用第三个字符替换它。对于 例如,如果'a'和'c'相邻,则可以用'b'代替。什么 是应用此操作可以产生的最小字符串 反复?
现在我编写了以下递归解决方案(远非高效),但希望将其转换为自上而下或自下而上的解决方案。
问题:我无法想出用于记忆的表格结构。即使我只需输出结果字符串的长度,如何在不实际解决问题的情况下解决它。字符串越来越少,所以我该如何存储它们?
DP解决方案或Memoization的任何提示都会很棒!
编辑很多人都提出了自上而下的memoization解决方案,请尝试自下而上。
#include <iostream>
#include <string>
using namespace std;
string reduce(string s)
{
if (s.length() <= 1)
return s;
int k;
char c = s[0];
string min = s;
for (k = 1; k < s.length() && c; ++k)
if (s[k] != c)
c = 0;
if (c)
return s;
if (s.length() == 2){
if (s[0] != 'a' && s[1] != 'a')
s[0] = 'a';
else if (s[0] != 'b' && s[1] != 'b')
s[0] = 'b';
else if (s[0] != 'c' && s[1] != 'c')
s[0] = 'c';
s.resize(1);
return s;
}
for (k = 1; k < s.length(); ++k){
string s1 = reduce(s.substr(0, k));
string s2 = reduce(s.substr(k));
if (s1.length() + s2.length() < min.length())
min = s1 + s2;
if (!s1.empty() && !s2.empty() && s1.back() != s2.front()){
if (s1.back() != 'a' && s2.front() != 'a')
s1.back() = 'a';
else if (s1.back() != 'b' && s2.front() != 'b')
s1.back() = 'b';
else if (s1.back() != 'c' && s2.front() != 'c')
s1.back() = 'c';
s1 = reduce(s1 + s2.substr(1));
if (s1.length() < min.length())
min = s1;
}
}
return min;
}
int main()
{
string input;
cin >> input;
cout << reduce(input) << endl;
return 0;
}
答案 0 :(得分:2)
我有点懒得想到这个问题,但是我会给你一个记忆的方法,这种方法通常都有用。
不是直接递归,而是引入相互递归。
std::string reduce(std::string const &s)
{
// ...
string s1 = reduce_memo(s.substr(0, k));
string s2 = reduce_memo(s.substr(k));
// ...
}
其中reduce_memo
维护一个哈希表,即unordered_map
,将子问题映射到他们的解决方案。
// static is incredibly ugly, but I'll use it here for simplicity
static std::unordered_map<std::string, std::string> memo;
std::string reduce_memo(std::string const &s)
{
try {
return memo.at(s);
} except (std::out_of_range const &) {
std::string r = reduce(s);
memo[s] = r;
return r;
}
}
在C ++ 98中编程时,请使用std::map
代替unordered_map
。
答案 1 :(得分:1)
您可以通过将reduce(s)
的结果存储在map<string,string>
中来记住您的解决方案。
string reduce(string s, map<string,string>& memo) {
if (memo.count(s)) {
return memo[s];
}
// The rest of your code follows...
memo[s] = min;
}
答案 2 :(得分:1)
这不能解决问题,但我注意到了:
if (s.length() == 2){
if (s[0] != 'a' && s[1] != 'a')
s[0] = 'a';
else if (s[0] != 'b' && s[1] != 'b')
s[0] = 'b';
else if (s[0] != 'c' && s[1] != 'c')
s[0] = 'c';
s.resize(1);
return s;
}
根据问题陈述不起作用:
我们可以使用两个 相邻的不同字符,并将其替换为第三个字符。
考虑字符串s = "bb"
。 s[0]
和s[1]
都不等于'a'
,这意味着条件s[0] != 'a' && s[1] != 'a'
将评估为true
字符串"bb"
。这适用于任何相同值的连续字符串,例如"bb"
,"cc"
。
也许在这种情况下你可以取两个连续字符的差异,并检查它们是否为非零。
答案 3 :(得分:0)
无论我从问题中理解了什么,解决方案应该是
输入的长度 - 如果所有输入字符都相同,如
aaaaa - 5
bbb - 3
和其他案例中的1个。
如果我错过了部分问题,请纠正我。
答案 4 :(得分:0)
绝对最小值为1
。但是,字符串和替换规则的细节可能会在1
和n
之间产生,其中n
是字符串的长度。
对于特定的示例,那么最小的可能是n/2
,因为你需要2个字符并将它们替换为1(无法进一步替换),所以即使你有"acacacacacacacac"
,也是最好的如果你仍然只能减少2倍。
答案 5 :(得分:0)
我在竞争性编程工作坊中解决了类似的问题, 事实上,你提出的解决方案还不够快。
我的解决方案是创建这样的载体:
string s;
cin >> s;
int length = s.length();
vector<vector<int> > v(length, vector<int>(length)); // 2d array of size length*length.
其中v[i][j]
将是i
的{{1}}到j
的子字符串可以达到的最小长度。
稍后你所要做的就是填充这个表,增加大小。 希望有所帮助。
答案 6 :(得分:0)
"a...a" (“a" * n) == > n
"b...b" (“b" * n) == > n
"c...c" (“c" * n) == > n
any other ==> 1 or 2 with my greedy algorithm.
如果我们在这个贪心算法中得到2,我无法证明它是输入字符串的最小结果。
贪心算法
while the last two is the same character { // the number of the same characters
find the last replaceable pair // at the tail will decrease until
reduce them // it become 1
}
while find the first replaceable pair
reduce them