P3426 [POI2005]SZA-Template 解题报告
一道思维难度较高的 KMP 题目,对 border 性质要求较高。
Description
给定一个字符串 , 求长度最小的前缀 满足"匹配"完 ,这里的"匹配"可以看原题目,不太好描述,建议根据样例手玩一下。
Solution 1
考虑 fail 树。 为 的长度。
我们发现一个前缀 能够满足题目要求"匹配", 则它必然是 的 border。 同时还要满足所有 border 为它的位置之间距离最大值小于等于 的长度。
那么我们可以从小到大枚举 的 border, 即从根往节点 走, 每次删除当前节点的子树, 用双向链表维护距离最大值。
Code 1

#include<bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') f = -f; c = getchar();} while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();} return x * f; } const int N = 5e5 + 10; int n, mx, nxt[N], pre[N], suf[N], to[N]; char s[N]; vector<int> E[N]; inline void chmax(int &x, int y) {x = max(x, y);} inline void del(int x) { if (x) { int tp = pre[x], ts = suf[x]; chmax(mx, ts - tp); suf[tp] = ts; pre[ts] = tp; } for (auto v : E[x]) if (v != to[x]) del(v); } int main () { scanf("%s", s + 1); n = strlen(s + 1); nxt[1] = 0; for (int i = 2, j = 0; i <= n; ++ 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 <= n; ++ i) E[nxt[i]].push_back(i); for (int i = n; i; i = nxt[i]) to[nxt[i]] = i; for (int i = 1; i <= n; ++ i) pre[i] = i - 1, suf[i] = i + 1; del(0); int ans = 1; while (mx > ans) del(ans), ans = to[ans]; printf("%d\n", ans); return 0; }
Solution 2
考虑 DP。用 表示长度为 的 的前缀的答案。
有一个很奇妙的性质就是 只可能有两种取值, 或者 。
感性上证明一下。设 为当前的前缀, 为 的最长border。
能够"匹配" 的必要条件就是能够与 相同或者能够和 "匹配"。因为除了 本身以外, 最长的可能匹配串就是 , 而只有能够匹配 ,才能和 匹配 。
那么什么时候 能取到 呢?我们显然能够"匹配"到长度为 的前缀和后缀,因此我们只要找到长度 的前缀能够匹配最远的距离 , 满足 即可。
Code 2

#include<bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') f = -f; c = getchar();} while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();} return x * f; } const int N = 5e5 + 10; int n, nxt[N], dp[N], vis[N]; char s[N]; int main () { scanf("%s", s + 1); n = strlen(s + 1); nxt[1] = 0; for (int i = 2, j = 0; i <= n; ++ i) { while (j && s[i] != s[j+1]) j = nxt[j]; if (s[i] == s[j+1]) ++ j; nxt[i] = j; } dp[1] = 1; vis[1] = 1; for (int i = 2; i <= n; ++ i) { dp[i] = i; if (vis[dp[nxt[i]]] >= i - nxt[i]) dp[i] = dp[nxt[i]]; vis[dp[i]] = i; } printf("%d\n", dp[n]); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】