题解P3426 [POI2005]SZA-Template
蒟蒻的博客链接:本题题解
KMP学习笔记链接:KMP
题目链接:传送门
Byteasar想在墙上涂一段很长的字符,他为了做这件事从字符的前面一段中截取了一段作为模版. 然后将模版重复喷涂到相应的位置后就得到了他想要的字符序列.一个字符可以被覆盖很多次,但是一个位置不能填不同的字符.做一个模版很费工夫,所以他想要模版的长度尽量小,求最小长度是多少。
大体思路:
先用kmp求出字符串的
让
先证明几个引理。
注明: 用来覆盖一个序列的模版一定是字符串的前缀,但在下文中有些地方称为“长度为XXX的串”,意思是"长度为XXX的前缀"。
引理一、f[i] 的取值只能为i 或者f[nxt[i]]
引理1.1
这个很显然。。因为用这个字符串本身一定能覆盖前
引理1.2
如果
引理1.3 若
证明:如果
因为必须有一个长度
这与
引理1.4 若
证明:当
由
所以珂以用前
因此
由引理1.2,
引理1.5 若
图与引理1.4类似,就不画了qwq。
因为
因此前
若
因为可以用长度为
因此珂以把覆盖前
此时
因此
综上,
由引理1.1和引理1.2,有
由引理1.3,有
由引理1.4和引理1.5,当
因此
引理二、f[i]=f[nxt[i]] 的充要条件是存在j \in [i-nxt[i],i) ,f[j]=f[nxt[i]]
引理2.1 引理二的充分性证明,即
在
因此可以证明引理二的充分性。
引理2.2 引理二的必要性证明,即
图与引理2.1的证明类似,就不画了qwq。
因为
所以
又因为
所以
由引理一,
由引理1.1,
所以
综上,
由引理2.1和引理2.2,引理二得证。
重复一次引理:
引理一、
引理二、
然后看这道题:
先kmp跑出
遍历1~n,若
毒瘤代码
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=500005;
int n,b[Size],f[Size],nxt[Size];
char str[Size];
int main() {
// n=read();
scanf("%s",str+1);
n=strlen(str+1);
int j=0;
for(re i=2; i<=n; i++) {
while(j>0 && str[j+1]!=str[i]) j=nxt[j];
if(str[j+1]==str[i]) j++;
nxt[i]=j;
}
for(re i=2; i<=n; i++) {
f[i]=i;
if(b[f[nxt[i]]]>=i-nxt[i]) {
f[i]=f[nxt[i]];
}
b[f[i]]=i;
}
printf("%d",f[n]);
return 0;
}