Solution [POI2006]OKR-Periods of Words
题目大意:给定一个字符串,求出对于所有前缀的最大周期长度之和。
KMP
分析:可以考虑这题在(fail)树上的性质
比较显然,一个前缀的最大周期长度,就等于前缀本身的长度减去最小非空(border)的长度
我们将(fail)树建出来,将虚拟(0)号点作为根,问题即为:对于每个节点,尽可能往上跳,不能到(0)点,到达点的编号之和(每个点代表一个前缀,点的编号即为前缀长度)
直接做复杂度爆炸,我们可以枚举(0)号点的每一个儿子,将它的编号乘以它的子树大小
复杂度就可以做到(O(n))
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 100;
char str[maxn];
int n,pi[maxn],siz[maxn];
vector<int> G[maxn];
ll ans;
inline void addedge(int from,int to){G[from].push_back(to);}
inline void dfs(int u){
siz[u] = 1;
for(int v : G[u])dfs(v),siz[u] += siz[v];
if(u == 0){
for(int v : G[u])ans -= 1ll * v * siz[v];
}
}
int main(){
scanf("%d",&n);
scanf("%s",str + 1);
for(int i = 2;i <= n;i++){
int j = pi[i - 1];
while(j > 0 && str[j + 1] != str[i])j = pi[j];
if(str[j + 1] == str[i])j++;
pi[i] = j;
}
for(int i = 1;i <= n;i++)addedge(pi[i],i);
ans = (1ll * n * (n + 1)) >> 1;
dfs(0);
printf("%lld
",ans);
return 0;
}