题目链接:bzoj3075
题目大意:
给你一个长度为n的字符串A,再给你一个长度为m的字符串B,求至少在A中删去多少个字符才能使得B不是A的子串。注:该题只读入A和B,不读入长度,先读入A,再读入B。数据保证A和B中只含小写字母。
题解:
AC自动机+DP
f[i][j]表示A串搞到第i位,树上走到j所能得到的串的最大长度。
首先按B串建树,拿A来走。对于A串中的一个字符有两种选择,删与不删。如果删,那么树上的位置仍与当前一样;如果不删,就走。。注意判断一下当前节点合不合法就是了。
#include<cstdio> #include<cstring> #include<cstdlib> #include<queue> #include<iostream> #include<algorithm> using namespace std; #define maxm 1010 struct node { int son[26],fail;bool bk; }tr[maxm]; int len,rt,l1,l2,f[maxm*10][maxm]; char s1[maxm*10],s2[maxm]; int new_node() { len++; memset(tr[len].son,0,sizeof(tr[len].son)); tr[len].fail=0;tr[len].bk=false; return len; } void bt() { int now=rt,i; for (i=0;i<l2;i++) { int x=s2[i]-'a'; if (!tr[now].son[x]) tr[now].son[x]=new_node(); now=tr[now].son[x]; } tr[now].bk=true; } queue<int> q; void build_AC() { int i; for (i=0;i<26;i++) if (tr[rt].son[i]) { tr[tr[rt].son[i]].fail=rt; q.push(tr[rt].son[i]); }else tr[rt].son[i]=rt; while (!q.empty()) { int x=q.front();q.pop(); int y=tr[x].fail; for (i=0;i<26;i++) if (tr[x].son[i]) { if (tr[y].son[i]) tr[tr[x].son[i]].fail=tr[y].son[i]; else tr[tr[x].son[i]].fail=rt; q.push(tr[x].son[i]); }else tr[x].son[i]=tr[y].son[i]; } } int mymax(int x,int y) {return (x>y)?x:y;} int main() { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); int i,j; len=0;rt=new_node(); gets(s1);l1=strlen(s1); gets(s2);l2=strlen(s2); bt();build_AC(); memset(f,-1,sizeof(f)); f[0][rt]=0;f[0][tr[rt].son[s1[0]-'a']]=1; for (i=0;i<l1-1;i++) for (j=1;j<=len;j++) if (f[i][j]!=-1) { int x=s1[i+1]-'a'; int y=tr[j].son[x]; if (!tr[y].bk) f[i+1][y]=mymax(f[i+1][y],f[i][j]+1); if (!tr[j].bk) f[i+1][j]=mymax(f[i+1][j],f[i][j]); } int ans=-1; for (i=1;i<=len;i++) if (f[l1-1][i]!=-1 && !tr[i].bk) ans=mymax(ans,f[l1-1][i]); printf("%d ",l1-ans); return 0; }