题目链接:P3426 [POI2005]SZA-Template - 洛谷 - 计算机科学教育新生态 ↗
设 表示前缀 的答案,设 表示以前缀 为印章,最远能覆盖到的位置。
容易发现,前缀 一定能被自己覆盖,因此 最大为 。
其何时能被更短的段覆盖?
考虑其 ,我们以覆盖其 相同的方式,也就是以 为印章长度,尝试覆盖 。
如果可行,那么 。
判断可行?
可行当且仅当:.
意思就是,以 为印章长度,至少要可以覆盖到 。
这段与 完全相同,一定能被覆盖,所以只需 之前能被覆盖。
#include <bits/stdc++.h>
using namespace std;
const int MAXLEN = 5e5 + 10;
char s[MAXLEN];
int len, nxt[MAXLEN];
void get_border() {
nxt[1] = 0;
for(int i = 2; i <= len; i++) {
int match = nxt[i - 1];
while(s[i] != s[match + 1] && match != 0) match = nxt[match];
if(s[i] == s[match + 1]) match++;
nxt[i] = match;
}
}
int dp[MAXLEN]; //dp[i]表示前缀[1, i]的答案
int lst[MAXLEN]; //lst[i]表示以前缀[1, i]为印章,最远能覆盖到的位置
int main() {
scanf("%s", (s + 1)); len = strlen(s + 1);
get_border();
dp[1] = 1; lst[1] = 1;
for(int i = 2; i <= len; i++) {
dp[i] = i;
if(lst[dp[nxt[i]]] >= i - nxt[i]) dp[i] = dp[nxt[i]];
lst[dp[i]] = i;
}
cout << dp[len] << endl;
return 0;
}
cpp