其他分享
首页 > 其他分享> > P3426 [POI2005]SZA-Template

P3426 [POI2005]SZA-Template

作者:互联网

对于字符串刻印章,使每个字符都能被印出来,并且不能印上其他字符。求印章最短长度。 \(|S|\leq 5\times 10^5\)。


看到题目第一感觉是KMP,但具体实现很费脑筋。

先举个例子:

1 2 3 4 5 6 7 8 9 10 11 12 13
s[i] a b a b b a b a b b a b a
nxt[i] 0 0 1 2 0 1 2 3 4 5 6 7 8

设 \(f[i]\) 表示前 \(i\) 个字符的答案,那么 \(f[i]\) 只有可能等于以下两种之一:

1.用一个长度为i的印章, \(f[i]=i\) 。

2.能盖住 \(nxt[i]\) 的印章有可能能盖住 \(s[1...i]\) , \(f[i]=f[nxt[i]]\) 我们具体分析这种情况:

首先,根据 \(f[nxt[i]]\) 的定义, \(s[1...nxt[i]]\) 一定能被覆盖,同时又因为 \(s[1...nxt[i]]=s[i-nxt[i]...i]\) ,所以只用考虑\(s[nxt[i]...i-nxt[i]]\) 的情况。最简单的方法就是看在 \(f[i-nxt[i]...i-1]\) 之间是否还有别的 \(f[j]=f[nxt[i]]\) ,如果有就可以更新 \(f[i]\) 。

#include<bits/stdc++.h>
using namespace std;

char s[500005];
int nxt[500005],f[500005],t[500005];

int main()
{
	scanf("%s",s+1);
	int slen=strlen(s+1);
	for(int i=2,j=0;i<=slen;++i)
	{
		while(j && s[i]!=s[j+1])
			j=nxt[j];
		if(s[i]==s[j+1])
			j++;
		nxt[i]=j;
	}
	for(int i=1;i<=slen;++i)
	{
		f[i]=i;
		if(t[f[nxt[i]]]>=i-nxt[i])
			f[i]=f[nxt[i]];
		t[f[i]]=i;
	}
	printf("%d",f[slen]);
	return 0;
}

标签:nxt,slen,...,POI2005,P3426,SZA,int,印章,500005
来源: https://www.cnblogs.com/zhouzizhe/p/16639052.html