F. x-prime Substrings(ac自动机+朴素dp)

题:https://codeforces.com/contest/1400/problem/F

题意:给定只含有1~9的字符串s,问最少删除多少个字符能让字符串不含x-prime Substrings(定义见题面)

分析:因为x<=20所以可以将所有x-prime Substrings找出来(x==20时只有29个),加到ac自动机上;

   然后就在trie树上进行dp转移,dp[i]表示 i 节点的最小值,用二维转移即可

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
#define pb push_back
typedef long long ll;
const int M=1e6+6;
const int inf=0x3f3f3f3f;
int trie[M][12];
int cntword[M];
int fail[M];
int cnt=0,x;
int dp[2][M];
vector<int>a;

void insert(){
    int root = 0;
    for(auto it:a){
        int next=it;
        if(!trie[root][next])
            trie[root][next] = ++cnt;
        root = trie[root][next];
    }
    cntword[root]++;
}
void getfail(){
    queue <int>q;
    for(int i=0;i<10;i++){
        if(trie[0][i]){
            fail[trie[0][i]] = 0;
            q.push(trie[0][i]);
        }
    }

    while(!q.empty()){
        int now = q.front();
        q.pop();

        for(int i=0;i<10;i++){
            if(trie[now][i]){

                fail[trie[now][i]] = trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else
                trie[now][i] = trie[fail[now]][i];
        }
    }
}

void dfs(int now){
    if(now==x){
        int n=a.size();
        int f=1;
        for(int i=0;i<n;i++){
            int sum=0;
            for(int j=i;j<n;j++){
                sum+=a[j];
                if(x%sum==0&&sum<x){///满足条件的x-prime Substrings
                    f=0;
                    break;
                }
            }
        }
        if(f)
            insert();
        return ;
    }
    for(int i=1;i<10&&now+i<=x;i++){
        a.push_back(i);
        dfs(now+i);
        a.pop_back();
    }
}
char s[M];
int main(){
    scanf("%s%d",s,&x);
    dfs(0);
    getfail();

    int len=strlen(s);
    for(int i=0;i<=cnt;i++)
        dp[0][i]=dp[1][i]=inf;
    dp[0][0]=0;
    int cur,nex;
    for(int i=0;i<len;i++){
        cur=i&1,nex=cur^1;
        for(int j=0;j<=cnt;j++)
            dp[nex][j]=inf;
        for(int j=0;j<=cnt;j++){
            dp[nex][j]=min(dp[nex][j],dp[cur][j]+1);///先假设要删去当前字符
            int v=trie[j][s[i]-'0'];
            if(!cntword[v])///要是不组成x-prime Substrings,则可无条件转移
                dp[nex][v]=min(dp[nex][v],dp[cur][j]);
        }
    }
    int ans=inf;
    for(int i=0;i<=cnt;i++)
        ans=min(ans,dp[nex][i]);
    printf("%d",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/starve/p/13577608.html