您的方法非常复杂。在非常糟糕的情况下,字符串a 将是aaaaaaaaabbbbbbbbb,而字符串b 将是ab,在这种情况下,您将需要O(|a|) 搜索,每个搜索都采用O(|a| + |b|)(假设使用一些复杂的搜索算法),导致总复杂度为O(|a|^2 + |a| * |b|),其约束条件为年。
对于他们的限制,一个很好的复杂性目标是O(|a| * |b|),大约 1 亿次操作,将在亚秒内完成。这是处理它的一种方法。对于字符串a 中的每个位置i,让我们计算最大长度n_i,使得a[i - n_i : i] = b[0 : n_i](换句话说,a 在该位置的最长后缀是b 的前缀)。我们可以使用Knuth-Morris-Pratt算法在O(|a| + |b|)中计算它。
在我们计算出n_i 之后,在a 中找到b 的第一个匹配项只需找到第一个等于|b| 的n_i。这将是a 中b 出现之一的右端。
最后,我们需要稍微修改一下 Knuth-Morris-Pratt。一旦我们计算出等于|b| 的n_i,我们就会在逻辑上删除b 的出现。考虑到从a 中删除了一些字母这一事实,我们将依赖这样一个事实,即 Knuth-Morris-Pratt 仅依赖于 n_i 的最后一个值(以及为 b 计算的值)和当前字母a,所以我们只需要一种快速的方法来检索 n_i 的最后一个值,然后我们在逻辑上删除 b 的出现。这可以通过存储n_i 的所有有效值的双端队列来完成。每个值将被推入双端队列一次,并从中弹出一次,因此维护它的复杂度为O(|a|),而 Knuth-Morris-Pratt 的复杂度为O(|a| + |b|),导致O(|a| + |b|) 的总复杂度。
这是一个 C++ 实现。它可能有一些错误,但它适用于您的样本,并且它适用于我在开头描述的最坏情况。
#include <deque>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
string a, b;
cin >> a >> b;
size_t blen = b.size();
// make a = b$a
a = b + "$" + a;
vector<size_t> n(a.size()); // array for knuth-morris-pratt
vector<bool> removals(a.size()); // positions of right ends at which we remove `b`s
deque<size_t> lastN;
n[0] = 0;
// For the first blen + 1 iterations just do vanilla knuth-morris-pratt
for (size_t i = 1; i < blen + 1; ++ i) {
size_t z = n[i - 1];
while (z && a[i] != a[z]) {
z = n[z - 1];
}
if (a[i] != a[z]) n[i] = 0;
else n[i] = z + 1;
lastN.push_back(n[i]);
}
// For the remaining iterations some characters could have been logically
// removed from `a`, so use lastN to get last value of n instaed
// of actually getting it from `n[i - 1]`
for (size_t i = blen + 1; i < a.size(); ++ i) {
size_t z = lastN.back();
while (z && a[i] != a[z]) {
z = n[z - 1];
}
if (a[i] != a[z]) n[i] = 0;
else n[i] = z + 1;
if (n[i] == blen) // found a match
{
removals[i] = true;
// kill last |b| - 1 `n_i`s
for (size_t j = 0; j < blen - 1; ++ j) {
lastN.pop_back();
}
}
else {
lastN.push_back(n[i]);
}
}
string ret;
size_t toRemove = 0;
for (size_t pos = a.size() - 1; a[pos] != '$'; -- pos) {
if (removals[pos]) toRemove += blen;
if (toRemove) -- toRemove;
else ret.push_back(a[pos]);
}
reverse(ret.begin(), ret.end());
cout << ret << endl;
return 0;
}
[in] hehelllloworld
[in] hell
[out] oworld
[in] abababc
[in] ababc
[out] ab
[in] caaaaa ... aaaaaabbbbbb ... bbbbc
[in] ab
[out] cc