zoukankan      html  css  js  c++  java
  • Codeforces E94 F. x-prime Substrings (AC自动机 + dp)

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

    参考博客

    题意:

    给一个字符串s (仅由数字1~9构成) 和数x. 令$f(l,r) $ 表示 字符串l 到 r 位的数之和。同时定义 x-prime 为(f(l_{1}, r_{1}) = x) 且不存在(l_{2}, r_{2}) 同时满足以下要求

    • (l_{1} le l_{2} le r_{2} le r_{1}) ;
    • (f(l_{2}, r_{2}) != x)
    • x 可以被 (f(l2, r2)) 整除

    问最少删除几个字符,使得字符串s不含x-prime.

    题目大意:删除最少字符,使得字符串如果一个区间和等于x,不存在子区间和 val 能整除x且 val != x。

    题解:

    前置题目:HDU2457

    首先可以看到x很小,那完全可以dfs把所有不满足题意的字符串 (即含有x-prime) 都打表打出来。然后用这些字符串建AC自动机,然后在AC自动机上进行dp就可以了。

    为什么要这样做呢?这个问题做了hdu2457就能理解。这里主要讲一下dp过程。

    为什么用不满足的字符串建AC自动机: 其实用不满足的字符串建AC自动机,然后在”匹配“时只要不接触AC自动机带标记的点,那么这个不满足的串一定无法在s串中匹配上,那s串就是满足的串。

    DP讲解:用dp[i] [j]表示处理到s串第 i 位,此时AC自动机上对应 j 结点时为满足题意删除的最小数量。 那么我们现在肯定要根据已知的状态推 i + 1各个状态的答案。分两种情况。

    • 1 把i + 1位字符删掉,相当与在状态(i, j)基础上 i + 1 , 但AC自动机上当前对应的节点还是 j 没有变。删除数加1。 状态转移方程 : (dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 1) ​)
    • 2 不删除i + 1 位字符,那么在(i, j) 基础上只能i + 1, j只能在AC自动机上 j 节点下面的和s[i] 相同的节点移动。状态转移方程:(dp[i + 1][v] = min(dp[i + 1][v], dp[i][j])) 。 其中 (v = nxt[j][s[i] - '0'])

    代码:

    //2020/8/27/22:41
    //AC自动机 + dp
    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<queue>
    #include<vector>
    #include<string>
    #include<fstream>
    using namespace std;
    #define rep(i, a, n) for(int i = a; i <= n; ++ i);
    #define per(i, a, n) for(int i = n; i >= a; -- i);
    typedef long long ll;
    const int N = 1e3 + 10;
    const int M = 5e3 + 10;
    const int mod = 998244353;
    const double Pi = acos(- 1.0);
    const int INF = 0x3f3f3f3f;
    const int G = 3, Gi = 332748118;
    ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; }
    ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
    bool cmp(int a, int b){return a > b;}
    //
    
    struct Aho{
        int nxt[M][30] ,fail[M], mark[M];
        int size;
        
        queue<int> que;
        void init(){
            while(que.size()) que.pop();
            memset(nxt, 0, sizeof(nxt));
            memset(fail, 0, sizeof(fail));
            memset(mark, 0, sizeof(mark));
            size = 0;
        }
        
        //建立trie树
        void Insert(char *S){
            int n = strlen(S);
            int now = 0;
            for(int i = 0; i < n; ++ i){
                // cout<<S[i];
                int id = S[i] - '0';
                if(!nxt[now][id]) nxt[now][id] = ++ size;
                now = nxt[now][id];
            }
            mark[now] = 1;
            // cout<<endl;
        }
        
        //建立fail指针
        void Build(){
            fail[0] = -1; 
            for(int i = 0; i < 10; ++ i){
                if(nxt[0][i]) que.push(nxt[0][i]);
            }
            while(que.size()){
                int u = que.front(); que.pop();
                for(int j = 0; j < 10; ++ j){
                    int v = nxt[u][j];
                    if(!v) nxt[u][j] = nxt[fail[u]][j];
                    else{
                        que.push(v);
                        fail[v] = nxt[fail[u]][j];
                        mark[v] |= mark[fail[v]];
                    }
                }
            }
        }
    }aho;
    
    char s[M], t[M];
    int n, x;
    int dp[N][M];
    
    void dfs(int sum, int dep){
        // cout<<sum<<" "<<dep<<endl;
        if(sum > x) return;
        if(sum == x){
            t[dep] = 0;
            for(int i = 0; i < dep; ++ i){
                int val = 0;
                for(int j = i; j < dep; ++ j){
                    val += t[j] - '0';
                    if(sum % val == 0 && sum != val) return;
                }
            }
            aho.Insert(t);
        }
        for(int i = 1; i < 10; ++ i){
            t[dep] = i + '0';
            dfs(sum + i, dep + 1);
        }
    }
    
    int main()
    {
        scanf("%s%d",&s,&x);
        aho.init();
        dfs(0, 0);
        aho.Build();
        memset(dp, 0x3f, sizeof(dp));
        dp[0][0] = 0;
        n = strlen(s);
        for(int i = 0; i < n; ++ i){
            for(int j = 0; j <= aho.size; ++ j){
                if(dp[i][j] < INF){
                    dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 1);
                    int v = aho.nxt[j][s[i] - '0'];
                    if(aho.mark[v]) continue;
                   dp[i + 1][v] = min(dp[i + 1][v], dp[i][j]);
                }
            }
        }
        int res = INF;
        for(int i =  0; i <= aho.size; ++ i){
            res = min(res, dp[n][i]);
        }
        printf("%d
    ",res);
        return 0;
    }
    
    
    
  • 相关阅读:
    java进阶知识--File类
    java进阶知识--函数式接口
    java进阶知识--Lambda表达式、递归
    java进阶知识--线程池
    java进阶知识--线程安全
    java进阶知识--多线程入门
    java基础知识--异常
    java基础知识--可变参数
    mysql中如何不重复插入满足某些条件的重复的记录的问题
    有关map中使用iterate迭代器遍历的不保序问题和list remove(object)的细节问题
  • 原文地址:https://www.cnblogs.com/A-sc/p/13574830.html
Copyright © 2011-2022 走看看