题目链接: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\n",l1-ans);
return 0;
}