题解 - CodeForces 1898E Sofia and Strings
Published:
分析
注意到:对某个长度为 \(n\) 的子串进行排序操作,可以拆分成若干次对长度为 \(2\) 的子串排序的操作。具体地,对于子串 \(s_is_{i+1}\),如果 \(s_i>s_{i+1}\),调换两者顺序;否则不进行任何操作。所以,不妨化繁为简,每次只对长度为 \(2\) 的子串考虑排序操作。
基于上面的思考,若能构造出目标字符串,目标字符串中的每个元素 \(t_i\),要么是在原地没有因为排序移动过,要么被比它小的字符调换到右边,要么被比它大的字符调换到左边。形式化地,记 \(t_i\) 在 \(s\) 中的原始下标为 \(now_i\),那么对于所有满足 \(t_j<t_i\) 且 \(j>i\) 的 \(j\),有 \(now_j>now_i\),对于所有满足 \(t_j>t_i\) 且 \(j<i\) 的 \(j\),有 \(now_j<now_i\)。
不妨考虑贪心,从小到大循环 \(26\) 个小写字母,从右往左填入当前循环的字母,记录 \(now\) 在当前位置的后缀最小值 \(mi\),最大化当前位置的 \(now_i\),使得 \(now_i< mi\)。如果不存在这样的 \(now_i\),则无法构造目标字符串。具体实现时可以分别预先记录每个字母在原字符串、目标字符串中的位置,方便填入。时间复杂度接近 \(O(\alpha\cdot m)\),本题 \(\alpha=26\)。
C++实现
// C++11 or later
#include <iostream>
#include <cstdio>
#include <vector>
#include <utility>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll read() {
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
return x * f;
}
const int N = 2e5 + 10;
int n, m, now[N];
char given[N], target[N];
// v1记录每个字母在原字符串中的位置,v2记录每个字母在目标字符串中的位置
vector<int> v1[26], v2[26];
bool sol() {
for (int i = 0; i <= 25; i++) v1[i].clear(), v2[i].clear();
n = read(), m = read();
scanf("%s", given + 1);
scanf("%s", target + 1);
memset(now, 0x7f, sizeof(int) * (m + 2));
for (int i = 1; i <= n; i++)
v1[given[i] - 'a'].push_back(i);
for (int i = 1; i <= m; i++)
v2[target[i] - 'a'].push_back(i);
// 循环26个字母:
for (int i = 0; i <= 25; i++) {
if (v1[i].size() < v2[i].size()) return false;
if (v2[i].empty()) continue;
auto p1 = v1[i].rbegin(), p2 = v2[i].rbegin();
int mi = 0x7f7f7f7f;
for (int j = m; j >= 1; j--) {
if (p1 == v1[i].rend()) return false;
mi = min(now[j], mi);
// 试图使即将填入的now[i]小于mi
if (mi < *p1) {
while (p1 != v1[i].rend())
if (*p1 > mi) p1++;
else break;
if (p1 == v1[i].rend()) return false;
}
if (j == *p2) now[*(p2++)] = *(p1++);
if (p2 == v2[i].rend()) break; // 当前字母已经填完了
}
}
return true;
}
void tc() {
int t = read();
while (t--) {
if (sol()) printf("YES\n");
else printf("NO\n");
}
}
int main() {
tc();
return 0;
}
