其他分享
首页 > 其他分享> > LGP3426题解

LGP3426题解

作者:互联网

真是不管什么时候来做这道题都会觉得很神仙呐。。。

观察一下,如果存在一个合法的印章,那么这个印章一定是这个串的前缀,也是这个串的后缀。

即合法的印章一定是原串的 \(\rm Border\)。

于是设 \(dp[n]\) 为 \([1,n]\) 这个前缀的最短的印章长度。这个印章就是 \([1,dp[n]]\)。

注意到 \(dp[fail[n]]\) 的长度不可能大于 \(dp[n]\),所以 \(dp[n]\) 其实只有两种决策:\(n\) 和 \(dp[fail[n]]\)。

于是开始思考满足什么条件的情况下才能够使用 \(dp[fail[n]]\)。

注意到 \([1,dp[fail[n]]]\) 一定是 \([1,n]\) 的后缀,所以我们假设这个印章已经把这个后缀狂暴轰入了,那么 \([1,n-dp[fail[n]]]\) 还没有被轰入。所以条件就是 \([1,n-dp[fail[n]]+1]\sim [1,n]\) 这几个前缀中的某一个的前缀的 \(dp\) 值为 \(dp[fail[n]]\)。(也就是轰入这个前缀之后,再使用这个印章轰入后缀。)

看似需要维护区间 \(\max\)?其实并不需要,只需要对每一个 \(dp\) 值维护最右边的下标即可。

代码很短,但是思维量是很多的。

#include<cstring>
#include<cstdio>
typedef unsigned ui;
const ui M=5e5+5;
ui n,dp[M],mx[M],fail[M];char s[M];
signed main(){
	ui i,j;scanf("%s",s+1);n=std::strlen(s+1);
	for(i=1,j=0;i<=n;++i){
		if(i==1)fail[i]=0,dp[i]=1,mx[1]=1;
		else{
			while(j&&s[j+1]!=s[i])j=fail[j];if(s[j+1]==s[i])++j;fail[i]=j;
			if(mx[dp[j]]>=i-j)dp[i]=dp[j];else dp[i]=i;mx[dp[i]]=i;
		}
	}
	printf("%u",dp[n]);
}

标签:LGP3426,前缀,题解,印章,ui,轰入,fail,dp
来源: https://www.cnblogs.com/lmpp/p/15852370.html