从空格分隔的单词的输入,如何连接连续的单词,以便:
示例输入:
一只猫会吃一只老鼠
最小长度示例:
L = 5
解决第一个条件而不是第二个条件的朴素算法:
当组的长度小于L时,将下一个单词连接到组
如果最后一个组短于L,则将最后两个组连接在一起
这种天真的算法产生:
第二个条件没有解决,因为更好的解决方案是:
哪种算法可以解决作为程序执行速度相对较快的第二个条件(最小最长组)?(快速,我希望避免测试所有可能的组合)
我知道C,ObjC,Swift,Javascript,Python,但伪代码很好。
答案 0 :(得分:3)
这可以通过动态编程方法完成。让我们计算一个函数F(i)
- 在第一个i
个单词的正确分割中成为最长组的最小长度。
F(0) = 0
F(i) = Min(Max(F(j), totalLen(j+1, i))), for j in [0..i-1]
哪里
totalLen(i, j) = total length of words from i to j, if the length is at least L
totalLen(i, j) = MAX, if total length is less than L
答案是F(n)
的价值。要自己获取群组,我们可以为每个j
保存最佳i
的索引。
c ++中有从头开始的实现:
const vector<string> words = {"would", "a", "cat", "eat", "a", "mouse"};
const int L = 5;
int n = words.size();
vector<int> prefixLen = countPrefixLen(words);
vector<int> f(n+1);
vector<int> best(n+1, -1);
int maxL = prefixLen[n];
f[0] = 0;
for (int i = 1; i <= n; ++i) {
f[i] = maxL;
for (int j = 0; j < i; ++j) {
int totalLen = prefixLen[i] - prefixLen[j];
if (totalLen >= L) {
int maxLen = max(f[j], totalLen);
if (f[i] > maxLen) {
f[i] = maxLen;
best[i] = j;
}
}
}
}
output(f[n], prev, words);
预处理和输出细节:
vector<int> countPrefixLen(const vector<string>& words) {
int n = words.size();
vector<int> prefixLen(n+1);
for (int i = 1; i <= n; ++i) {
prefixLen[i] = prefixLen[i-1] + words[i-1].length();
}
return prefixLen;
}
void output(int answer, const vector<int>& best, const vector<string>& words) {
cout << answer << endl;
int j = best.size()-1;
vector<int> restoreIndex(1, j);
while (j > 0) {
int i = best[j];
restoreIndex.push_back(i);
j = i;
}
reverse(restoreIndex.begin(), restoreIndex.end());
for (int i = 0; i+1 < restoreIndex.size(); ++i) {
for (int j = restoreIndex[i]; j < restoreIndex[i+1]; ++j) {
cout << words[j] << ' ';
}
cout << endl;
}
}
输出:
6
would a
cat eat
a mouse
Runnable:https://ideone.com/AaV5C8
进一步改善
此算法的复杂性为O(N^2)
。如果数据太慢,我可以建议一个简单的优化:
让内循环反转。首先,这允许摆脱prefixLen
数组及其预处理,因为现在我们逐个向组添加单词(实际上,我们可以在初始版本中摆脱这种预处理,但是以简单为代价)。更重要的是,当totalLen
不小于已经计算的f[i]
时,我们可以打破循环,因为进一步的迭代永远不会导致改进。内循环的代码可以更改为:
int totalLen = 0;
for (int j = i-1; j >= 0; --j) {
totalLen += words[j].length();
if (totalLen >= L) {
int maxLen = max(f[j], totalLen);
if (f[i] > maxLen) {
f[i] = maxLen;
best[i] = j;
}
}
if (totalLen >= f[i]) break;
}
这可以极大地提高L
的非常大的值的性能。