F. x-prime Substrings(AC自动机 + dp)

题意:你被给予了一个整数值x还有一个由1~9的数字组成的字符串。

让我们定义(f(l,r))(s[l...r])之间的数字和。

让我们称一个子串(s[l_{1}...r_{1}])(x-prime)的,如果
(f(l_{1}, r_{1}) = x)
不存在值(l_{2}, r_{2})使得
(l_{1} <= l_{2} <= r_{2} <= l_{1})
(f(l_{2}, r_{2}) != x)
(x被f(l_{2}, r_{2})整除)

你可以擦除这个字符串中的一些字符。如果你擦除了一个字符,那么剩余两部分则会合并成一个字符串。
求最小的擦除数量,使得这个字符串不包含(x-prime)

分析:ac自动机dp。我们可以暴搜搜出所有不合法的子串(最多有2500个子串,每个字符的最长长度为20),然后建立成一棵trie树。我们可以定义如下的状态dp[i][j]:考虑到当前字符串的第i个字符,并且已经匹配到AC自动机的第j个节点的最小擦除数量。那么有两种决策,首先是擦除当前字符,那么dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 1);即考虑到下个字符i + 1,依然匹配到第j个节点,那么第i个字符是被擦除的,加上权值1。第二种操作是不擦除,那么我们的AC自动机如果有一条出边(tr[i][j]指向一个节点q),并且这个q没有(x-prime)子串,那么我们就可以进行一次转移,转移方程是dp[i + 1][q] = min(dp[i + 1][q], dp[i][j]);

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1005;
//最多的情况子串个数
const int M = 2500;
char s[N];
char str[20];
int tr[M * 20][10];
int q[M * 20];
int x, idx;
int ne[M * 20];

//考虑到当前第i个字符,走到ac自动机的第j个字符需要擦去的字符个数
int dp[N][20 * M];

bool match[M * 20];
void insert(int pos)
{
    int p = 0;

    for (int i = 0; i < pos; ++i)
    {
        int t = str[i] - '0';
        if (!tr[p][t])
            tr[p][t] = ++idx;
        p = tr[p][t];
    }
    match[p] = true;
}

void build()
{
    int hh = 0, tt = -1;
    for (int i = 1; i <= 9; ++i)
    {
        if (tr[0][i])
            q[++tt] = tr[0][i];
    }
    while (hh <= tt)
    {
        int t = q[hh++];
        for (int i = 1; i <= 9; ++i)
        {
            int p = tr[t][i];
            if (!p) tr[t][i] = tr[ne[t]][i];
            else
            {
                ne[p] = tr[ne[t]][i];
                q[++tt] = p;
            }
        }
    }
}

bool check(char str[], int pos)
{
    for (int i = 0; i < pos; ++i)
    {
        int sum = 0;
        for (int j = i; j < pos; ++j)
        {
            sum += str[j] - '0';
            if (sum != x && x % sum == 0)
                return false;
        }
    }
    return true;
}


void dfs(char str[], int pos, int sum)
{
    if (sum > x)
        return;
    if (sum == x)
    {
        if (check(str, pos))
        {
            insert(pos);
        }
    }

    for (int d = 1; d <= 9 && sum + d <= x; ++d)
    {
        str[pos] = char(d + '0');
        dfs(str, pos + 1, sum + d);
    }
}

int main() {
   
    scanf("%s", s + 1);
    int len = strlen(s + 1);
   
    scanf("%d", &x);

    dfs(str, 0, 0);

    build();

    memset(dp, 0x3f, sizeof dp);

    dp[1][0] = 0;
    for (int i = 1; i <= len; ++i)
    {
        int c = s[i] - '0';
        for (int j = 0; j <= idx; ++j)
        {
            int q = tr[j][c];

            if (dp[i][j] != 0x3f3f3f3f)
            {
                //擦去字符
                dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 1);
                if (!match[q]) dp[i + 1][q] = min(dp[i + 1][q], dp[i][j]);
            }            
        }
    }

    int res = 0x3f3f3f3f;

    for (int i = 0; i <= idx; ++i) res = min(res, dp[len + 1][i]);
    printf("%d
", res);

    return 0;
}
原文地址:https://www.cnblogs.com/pixel-Teee/p/13577164.html