对于字符串刻印章,使每个字符都能被印出来,并且不能印上其他字符。求印章最短长度。 ∣ S ∣ ≤ 5 × 1 0 5 |S|\leq 5\times 10^5 ∣S∣≤5×105。
看到题目第一感觉是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 ] f[i] f[i] 表示前 i i i 个字符的答案,那么 f [ i ] f[i] f[i] 只有可能等于以下两种之一:
1.用一个长度为i的印章, f [ i ] = i f[i]=i f[i]=i 。
2.能盖住 n x t [ i ] nxt[i] nxt[i] 的印章有可能能盖住 s [ 1... i ] s[1...i] s[1...i] , f [ i ] = f [ n x t [ i ] ] f[i]=f[nxt[i]] f[i]=f[nxt[i]] 我们具体分析这种情况:
首先,根据 f [ n x t [ i ] ] f[nxt[i]] f[nxt[i]] 的定义, s [ 1... n x t [ i ] ] s[1...nxt[i]] s[1...nxt[i]] 一定能被覆盖,同时又因为 s [ 1... n x t [ i ] ] = s [ i − n x t [ i ] . . . i ] s[1...nxt[i]]=s[i-nxt[i]...i] s[1...nxt[i]]=s[i−nxt[i]...i] ,所以只用考虑 s [ n x t [ i ] . . . i − n x t [ i ] ] s[nxt[i]...i-nxt[i]] s[nxt[i]...i−nxt[i]] 的情况。最简单的方法就是看在 f [ i − n x t [ i ] . . . i − 1 ] f[i-nxt[i]...i-1] f[i−nxt[i]...i−1] 之间是否还有别的 f [ j ] = f [ n x t [ i ] ] f[j]=f[nxt[i]] f[j]=f[nxt[i]] ,如果有就可以更新 f [ i ] f[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;
}