zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    HDU 6182 A Math Problem 水题
    HDU 6186 CS Course 位运算 思维
    HDU 6188 Duizi and Shunzi 贪心 思维
    HDU 2824 The Euler function 欧拉函数
    HDU 3037 Saving Beans 多重集合的结合 lucas定理
    HDU 3923 Invoker Polya定理
    FZU 2282 Wand 组合数学 错排公式
    HDU 1452 Happy 2004 数论
    HDU 5778 abs 数论
    欧拉回路【判断连通+度数为偶】
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13577164.html
Copyright © 2011-2022 走看看