• 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;
    }
    
  • 相关阅读:
    Queue Aizu
    Stack Aizu
    Stack Aizu
    Shell Sort Aizu
    Shell Sort Aizu
    Stable Sort Aizu
    VS2013下.Net Framework4配置FineUI4.14
    VS2013下.Net Framework4配置FineUI4.14
    Ubuntu-14.04.1 desktop安装时遇到的小问题
    Ubuntu-14.04.1 desktop安装时及安装后遇到的小问题
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13577164.html
Copyright © 2020-2023  润新知