删减
删减
农夫约翰把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过 ${10}^{6}$ 的字符串 $S$。
他希望从 $S$ 中删除子串 $T$。
农夫约翰在 $S$ 中从头开始寻找子串 $T$,一旦找到,就将它从 $S$ 中删除,然后再次从头开始寻找(而不是接着往下找)。
他重复这个操作直到 $S$ 中没有子串 $T$ 为止。
注意,删除一个子串 $T$ 可能会导致一个新的子串 $T$ 的出现。
请帮助约翰完成这些操作并输出最后的 $S$。
输入格式
第一行包含字符串 $S$。
第二行包含字符串 $T$。
输出格式
输出操作完成后的 $S$。
保证最终字符串 $S$ 不为空。
数据范围
$S$ 的长度不超过 ${10}^{6}$,$T$ 的长度不超过 $100$ 且不超过 $S$ 的长度,字符串中只出现小写字母。
输入样例:
whatthemomooofun
moo
输出样例:
whatthefun
解题思路
题意很简单,但一开始连暴力都想不到怎么写。
暴力的做法是遍历字符串中的每一个字符,然后以这个字符为左端点,长度与匹配串相同的字串,与匹配串进行匹配,如果相同就从字符串中删除这个字串,此时指针就指向删除的子串的右端点的下一个位置。然后将指针往前移动匹配串的长度,重复上面的步骤,直到指针越界或者无法进行匹配。
首先删除函数的时间复杂度就为$O \left( n \right)$,其实就是把后面的字符一个一个移动到前面。一共最多会删除$\frac{n}{m}$次,因此这种方法的时间复杂度为$O \left( \frac{n^{2}}{m} \right)$。必然会超时。
一开始暴力做法还可以过,不过后面数据加强了,现在过不了了。
TLE代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <string> #include <algorithm> using namespace std; const int N = 1e6 + 10; char str[N]; int main() { scanf("%s", str); string s(str); scanf("%s", str); string t(str); for (int i = 0; i < s.size(); i++) { while (i >= 0 && s.substr(i, t.size()) == t) { s.erase(i, t.size()); i -= t.size(); if (i < 0) i = -1; } } printf("%s", s.c_str()); return 0; }
正解是用栈来模拟。栈的作用是可以优化删除的操作。
做法是遍历字符串,把字符压入栈中,每加入一个字符就判断从栈顶往后的$m$个字符(假设模式串的长度为$m$)是否等于模式串,如果是的话就将栈顶的$m$个字符弹出。时间复杂度为$O \left( n \times m \right)$。
AC代码如下:
1 #include <cstdio> 2 #include <iostream> 3 #include <string> 4 #include <algorithm> 5 using namespace std; 6 7 8 int main() { 9 string s, t; 10 cin >> s >> t; 11 12 string stk; // 栈的类型是string 13 for (auto &it : s) { 14 stk += it; 15 if (stk.size() >= t.size() && stk.substr(stk.size() - t.size()) == t) { 16 stk.erase(stk.size() - t.size(), t.size()); 17 } 18 } 19 20 cout << stk; 21 22 return 0; 23 }
参考资料
AcWing 1883. 删减(春季每日一题2022):https://www.acwing.com/video/3823/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16156809.html