【CF 1051E】 Vasya and Big Integers
题意:
给出一个字符串\(a\),问有多少种划分方式使得划出来的每一段中的数值含义大于等于\(L\)并且小于等于\(R\)。
不允许没有前导\(0\)。
思路:
考虑\(f[i]\)表示前\(i\)个字符中有多少种合法方案,那么考虑向后\(dp\),也就是枚举每一个开头\(i + 1\),如果不是前导\(0\),那么找到最近的大于等于\(L\)的\(l\),以及最远的小于等于\(R\)的\(r\)。
那么这段区间的贡献加上\(f[i]\)即可。
怎么判断是否大于等于\(l\),或者小于等于\(R\)?
考虑一个字符串数值大小的判断显然是从\(LCP\)的后一位那里决定的,那么用\(ExKMP\)求出\(a\)与\(L\)以及\(a\)与\(R\)的\(Extend\)即可。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 2000010
const int p = 998244353;
char s[N], L[N], R[N];
ll sum[N];
struct ExKMP {
int Next[N];
int extend[N];
//下标从1开始
void get_Next(char *s) {
int lens = strlen(s + 1), p = 1, pos;
//Next[1]要特殊考虑
Next[1] = lens;
while (p + 1 <= lens && s[p] == s[p + 1]) ++p;
//Next[2]初始化
Next[pos = 2] = p - 1;
for (int i = 3; i <= lens; ++i) {
int len = Next[i - pos + 1];
//第一种情况
if (len + i < p + 1) Next[i] = len;
//第二种情况
else {
//找到对于子串最靠后已经匹配的位置
int j = max(p - i + 1, 0);
//暴力匹配
while (i + j <= lens && s[j + 1] == s[i + j]) ++j;
p = i + (Next[pos = i] = j) - 1;
}
}
}
void work(char *s, char *t) {
get_Next(t);
int lens = strlen(s + 1), lent = strlen(t + 1), p = 1, pos;
while (p <= lent && s[p] == t[p]) ++p;
p = extend[pos = 1] = p - 1;
for (int i = 2; i <= lens; ++i) {
int len = Next[i - pos + 1];
if (len + i < p + 1) extend[i] = len;
else {
int j = max(p - i + 1, 0);
while (i + j <= lens && j <= lent && t[j + 1] == s[i + j]) ++j;
p = i + (extend[pos = i] = j) - 1;
}
}
}
}exkmp[2];
template <class T1, class T2>
void add(T1 &x, T2 y) {
x += y;
if (x >= p) x -= p;
}
bool BigL(int l, int r) {
int LCP = exkmp[0].extend[l];
if (l + LCP - 1 >= r) return 1;
return s[l + LCP] > L[1 + LCP];
}
bool SmallR(int l, int r) {
int LCP = exkmp[1].extend[l];
if (l + LCP - 1 >= r) return 1;
return s[l + LCP] < R[1 + LCP];
}
int main() {
while (scanf("%s%s%s", s + 1, L + 1, R + 1) != EOF) {
int lens = strlen(s + 1), lenL = strlen(L + 1), lenR = strlen(R + 1);
exkmp[0].work(s, L); exkmp[1].work(s, R);
for (int i = 1; i <= lens; ++i) sum[i] = 0;
ll res = 1;
sum[1] = p - 1;
for (int i = 1; i <= lens; ++i) {
int l, r;
if (s[i] == '0') {
if (L[1] == '0') {
l = r = i;
} else {
add(res, sum[i]);
continue;
}
} else {
l = i + lenL - 1; if (!BigL(i, l)) ++l;
r = i + lenR - 1; if (!SmallR(i, r)) --r;
}
if (l <= r) {
add(sum[l], res);
add(sum[r + 1], p - res);
}
add(res, sum[i]);
}
printf("%lld\n", res);
}
return 0;
}

浙公网安备 33010602011771号