[USACO13MAR] 项链Necklace

项链Necklace

贝西收集了N颗石头,每颗石头上都有一个字母,贝西想把这些石头做成项链。

贝西的身边有另一只奶牛,这只奶牛的名字是一个长度为M的字符串,贝西不希望这只牛的名字出现在她的项链上(项链的子串),她想知道,最少删掉几颗石头就可以避免这种情况发生。

The_Virtuoso的题解

首先如果用AC自动机做这道题显然要把B串建在AC自动机上(AC自动机上就一个串好像有点浪费qwq)。要想B串不出现在A串中,只要把A串在AC自动机上跑,使它一直不遍历到B串的终止节点就能保证B串不是A串的子串。想要最优解自然要dp,那么就可以定义f[i][j]表示A串的第i个字符匹配到了AC自动机上第j个节点保留的最长长度。对于A串上的每一个字符可以删除或者在AC自动机上往下走,最后用A串总长len减掉max{f[len][i]}就是最小删除数了。

时间复杂度\(O(N M)\)

co int N=1e4+1,M=1e3+1;
char s[N],t[M];
namespace AC
{
	int tot;
	int ch[M][26],val[M];
	int fail[M];
	
	void ins(char s[],int n)
	{
		int u=0;
		for(int i=0;i<n;++i)
		{
			int k=s[i]-'a';
			if(!ch[u][k])
				ch[u][k]=++tot;
			u=ch[u][k];
		}
		val[u]=1;
	}
	
	void getfail()
	{
		std::queue<int>Q;
		for(int i=0;i<26;++i)
			if(ch[0][i])
				Q.push(ch[0][i]);
		while(Q.size())
		{
			int u=Q.front();Q.pop();
			for(int i=0;i<26;++i)
			{
				if(ch[u][i])
				{
					fail[ch[u][i]]=ch[fail[u]][i];
					Q.push(ch[u][i]);
				}
				else
					ch[u][i]=ch[fail[u]][i];
			}
		}
	}
	
	int f[N][M];
	
	void solve(char s[],int n)
	{
		for(int i=0;i<n;++i)
		{
			int k=s[i]-'a';
			for(int j=0;j<=tot;++j)
			{
				if(!val[ch[j][k]])
					f[i+1][ch[j][k]]=std::max(f[i+1][ch[j][k]],f[i][j]+1);
				if(!val[j])
					f[i+1][j]=std::max(f[i+1][j],f[i][j]);
			}
		}
		int ans=0;
		for(int i=0;i<=tot;++i)
			ans=std::max(ans,f[n][i]);
		printf("%d\n",n-ans);
	}
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	scanf("%s",s);
	scanf("%s",t);
	AC::ins(t,strlen(t));
	AC::getfail();
	AC::solve(s,strlen(s));
	return 0;
}

posted on 2019-01-26 08:25  autoint  阅读(303)  评论(0编辑  收藏  举报

导航