CF1400F - x-prime Substrings

1400F - x-prime Substrings

首先发现 \(x\) 很小,所以发现对应的 x-prime 字符串数也很少,最多的情况是 \(x = 19\),有 2399 个,先爆搜出来。

现在问题变成了,删去最少的字符,使字符串内没有这些模式串。

那么建 AC 自动机,把模式串终点标记,即在非终点行走,每次可以跳过字符,求跳过的最小值。

DP 一下,设 \(f_{i, u}\) 为前 \(i\) 个字符,当前在 AC 自动机上的 \(u\) 号节点,其中没有 x-prime 子串,删除字符的最小值。

  • 有边 \((u, v)\),即从 \(u\)\(s[i + 1]\) 字符到 \(v\),若 \(v\) 点合法,即 \(f_{i + 1, v} = f_{i, u}\)
  • 也可以不走,原地不动,\(f_{i + 1, u} = f_{i, u} + 1\)

滚动数组可以滚掉第一维,这样空间就够了。最坏 \(x = 19\) 时 AC 自动机有 5000 左右的点数。

最坏复杂度 \(5000 \times 1000 = 5\times 10^6\)。跑得过。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
 
typedef long long LL;
 
const int N = 2005, S = 5005, INF = 0x3f3f3f3f;
 
int n, x, d[N], cnt, tr[S][9], fail[S];
int idx, f[S], g[S], q[S];
 
bool match[S];
 
char s[N];
 
bool inline check(int m) {
    for (int i = 1; i <= m; i++) {
        int s = 0;
        for (int j = i; j <= m; j++) {
            s += d[j];
            if (s != x && x % s == 0) return false;
        }
    }
    return true;
}
 
void inline insert(int m) {
	int p = 0;
	for (int i = 1; i <= m; i++) {
		int ch = d[i] - 1;
		if (!tr[p][ch]) tr[p][ch] = ++idx;
		p = tr[p][ch];
	}
	match[p] = true;
}
 
void dfs(int u, int rest) {
	if (!rest) {
	    if (check(u - 1)) insert(u - 1);
	    return;
	}
	for (int i = 1; i <= min(rest, 9); i++) {
		d[u] = i;
		dfs(u + 1, rest - i);
	}
}
 
void inline build() {
	int hh = 0, tt = -1;
    for (int i = 0; i < 9; i++)
    	if (tr[0][i]) q[++tt] = tr[0][i];
   	while (hh <= tt) {
   		int u = q[hh++];
   		for (int i = 0; i < 9; i++) {
   			int &v = tr[u][i];
   			if (!v) v = tr[fail[u]][i];
   			else fail[v] = tr[fail[u]][i], q[++tt] = v;
   		}
   	}
}
 
int main() {
	scanf("%s%d", s + 1, &x);
	n = strlen(s + 1);
	dfs(1, x);
	build();
	memset(f, 0x3f, sizeof f); f[0] = 0;
	for (int i = 1; i <= n; i++) {
		int ch = s[i] - '1';
		for (int j = 0; j <= idx; j++) g[j] = f[j], f[j] = INF;
		for (int j = 0; j <= idx; j++) {
			if (g[j] != INF) {
				f[j] = min(f[j], g[j] + 1);
				int v = tr[j][ch];
				if (!match[v]) f[v] = min(f[v], g[j]);
			}
		}
 	}
 	int ans = 2e9;
 	for (int i = 0; i <= idx; i++) ans = min(ans, f[i]);
	printf("%d\n", ans);
	return 0;
}
posted @ 2020-08-26 13:18  DMoRanSky  阅读(321)  评论(0编辑  收藏  举报