我怎么这么多天没写题解了,快来水一篇。
考虑对 \(s\) 串建子序列自动机后 dp。
设 \(n = |s|\)。建子序列自动机,就是求出 \(nxt_{i, j}\) 为 \([i, n]\) 中第一个等于 \(j\) 的位置,不存在则 \(nxt_{i, j} = n + 1\)。
然后我们设 \(f_{i, j}\) 为填到密码的第 \(i\) 位,当前匹配到 \(s\) 串的第 \(j\) 位是否存在。注意我们贪心匹配最前能匹配的位置。注意如果 \(j = n + 1\) 说明匹配不完,即密码已经不是一个 \(s\) 的子序列。
转移枚举当前位填的数位 \(d\),根据子序列自动机可以求得 \([j + 1, n]\) 中第一个 \(d\) 的出现位置,就能转移了。
答案是 \(f_{m, n + 1}\)。
code
// Problem: C. Strong Password
// Contest: Codeforces - Educational Codeforces Round 151 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1845/problem/C
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 300100;
int n, m, nxt[maxn][11], f[11][maxn];
char s[maxn], a[maxn], b[maxn];
void solve() {
scanf("%s%d%s%s", s + 1, &m, a + 1, b + 1);
n = strlen(s + 1);
for (int i = 0; i < 10; ++i) {
nxt[n + 1][i] = n + 1;
}
for (int i = n; i; --i) {
for (int j = 0; j < 10; ++j) {
nxt[i][j] = nxt[i + 1][j];
}
nxt[i][s[i] - '0'] = i;
}
for (int i = 0; i <= m; ++i) {
for (int j = 0; j <= n + 1; ++j) {
f[i][j] = 0;
}
}
f[0][0] = 1;
for (int i = 1; i <= m; ++i) {
for (int j = 0; j <= n + 1; ++j) {
if (f[i - 1][j]) {
for (int k = a[i] - '0'; k <= b[i] - '0'; ++k) {
f[i][j == n + 1 ? n + 1 : nxt[j + 1][k]] = 1;
}
}
}
}
puts(f[m][n + 1] ? "YES" : "NO");
}
int main() {
int T = 1;
scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}