【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;
}
posted @ 2019-07-28 09:08  Dup4  阅读(215)  评论(0)    收藏  举报