码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-09-08 20:46:23      阅读:47      评论:0      收藏:0      [点我收藏+]

标签:最小   res   字符串   分析   cpp   main   包含   prime   als   

题意:你被给予了一个整数值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\n", res);

    return 0;
}

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

标签:最小   res   字符串   分析   cpp   main   包含   prime   als   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!