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; }