Expenditure Reduction
复盘一下周赛的一道题,磕了很久花了大概3h去做呜呜呜,还是有必要复盘一下的。
题意很简单给两个字符串s和t,删掉s的一段前缀和s的一段后缀,仍然保证t是s的子序列的条件下的最短字符串。
最开始第一眼看到就想着去用KMP或双指针去做,但推了推发现都不可行,最后认真想过发现用dp去维护字符串的前缀不用考虑中间的状态,只用关心结尾是不是以字符串t最后一个字符为结尾就好了。
首先定义字符串t结尾的字符为tt,那么对于字符串s他可能会出现很多个tt,这样就会妨碍我们去更新dp的状态,所以我们可以把下标和字符绑在一起存起来,这样我们维护去维护dp状态的时候就会很轻松,那么问题是怎们去把下标和字符绑定起来呢,首先字符串中的字符有'0'-'9'和'a'-'z'总共有36个字符我们把他们转化为数字,ASCII码转化的话遍历的时候并不太好遍历,所以我们可以写个函数把这些字符转化为0-35的数字此函数如下:
std::function<int(char)> vule = [&](char c) {
if (c <= '9' && c >= '0')return c - '0';
else return c - 'a' + 10;
};
那么接下来就是下标与s中的字符一一绑定,可以用stl容器去处理,这里我选择的是用二维数组去存储,另外可以考虑到的是我们要找的是包含t的最短子串,那么我们肯定要处理最近的那个,最短子串最开始的地方也要不断地去更新开始的位置即可。那么预处理所有前缀状态的dp如下:
for (int i = 1; i <= lens; i++) {
for (int j = 0; j < 36; j++) {
dp1[i][j] = dp1[i - 1][j];
dp1[i][vule(s[i])] = i;
}
}
预处理字符串s的dp做完之后,就进入最终答案dp了,定义字符串t的首个字符是t0最后的dp要注意更新s字符串中所有t0字符状态,因为后面肯定要找头再找尾。
char head = t[1];
for (int i = 1; i <= lens; i++) {
if (s[i] == head) {
dp2[1][i] = i;
}
}
最后就是dp完整的状态转移了 ,再说一下这个dp2[i][j]状态表达,i代表字符串t[1-i]的前缀状态,j代表以s[j]字符为结尾的字符串状态;
for (int i = 2; i <= lent; i++) {
for (int j = 1; j <= lens; j++) {
int ch = vule(t[i - 1]);
if (s[j] == t[i] && dp1[j - 1][ch]) {
dp2[i][j] = dp2[i - 1][dp1[j - 1][ch]];
}
}
}
最后就是遍历状态中最短的字符串就可以了;
int r = 0, ans = 1e9;
for (int i = 1; i <= lens; i++) {
if (s[i] == t[lent] && dp2[lent][i] != -1) {
if (i - dp2[lent][i] + 1 < ans) {
ans = i - dp2[lent][i] + 1;
r = i;
}
}
}
另外千万别memset!千万别memset!!!千万别memset!!!会超时!!!会超时!!!会超时!!!会超时!!!
最后完整代码:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e5 + 9;
int dp1[N][40], dp2[150][N];
void solve() {
std::string s, t;
std::cin >> s >> t;
int lens = s.size(), lent = t.size();
s = "." + s;
t = "." + t;
// memset(dp1, -1, sizeof dp1);
// memset(dp2, -1, sizeof dp2);
for (int i = 1; i <= lent; i++) {
for (int j = 1; j <= lens; j++) {
dp2[i][j] = -1;
}
}
std::function<int(char)> vule = [&](char c) {
if (c <= '9' && c >= '0')return c - '0';
else return c - 'a' + 10;
};
for (int i = 1; i <= lens; i++) {
for (int j = 0; j < 36; j++) {
dp1[i][j] = dp1[i - 1][j];
dp1[i][vule(s[i])] = i;
}
}
char head = t[1];
for (int i = 1; i <= lens; i++) {
if (s[i] == head) {
dp2[1][i] = i;
}
}
for (int i = 2; i <= lent; i++) {
int k = vule(t[i - 1]);
for (int j = 1; j <= lens; j++) {
if (s[j] == t[i] && dp1[j - 1][k]) {
dp2[i][j] = dp2[i - 1][dp1[j - 1][k]];
}
}
}
int r = -1, ans = 1e9;
for (int i = 1; i <= lens; i++) {
if (s[i] == t[lent] && dp2[lent][i] != -1) {
if (i - dp2[lent][i] + 1 < ans) {
ans = i - dp2[lent][i] + 1;
r = i;
}
}
}
std::cout << s.substr(r - ans + 1, ans) << "\n";
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
比赛中 代码跑了886ms很极限了hh