1511: [POI2006]OKR-Periods of Words
https://www.lydsy.com/JudgeOnline/problem.php?id=1511
题意:
对于一个串的所有前缀,设为s,求出它的最大前缀Q,使得s为QQ的前缀。求最大前缀长度的和。
分析:
KMP+next数组。
next数组表示的是这个字符串的最大的公共前缀后缀。对于字符串s,设其next为j,那么它的前j个和后j个是相等的。如果这j个没有重叠,那么所求的最长前缀就是1~n-j。把这个前缀重复两遍可以包含s。所求的就是最小的这样border,最小的j(j越小,相当于n个字符,后j个更少,那么剩余的所求的前缀就越长了)。然后求出最小的公共前后缀。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 inline int read() { 6 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 7 for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 8 } 9 10 const int N = 1000100; 11 12 char A[N]; 13 int p[N]; 14 15 int main() { 16 int n; cin >> n; 17 scanf("%s",A+1); 18 p[1] = 0; 19 for (int i=2; i<=n; ++i) { 20 int j = p[i-1]; 21 while (j && A[i]!=A[j+1]) j = p[j]; 22 if (A[i] == A[j+1]) j++; 23 p[i] = j; 24 } 25 LL ans = 0; 26 for (int i=1; i<=n; ++i) { 27 int j = i; 28 while (p[j]) j = p[j]; // 这里看到这样写的p[p[j]],其实每个p[i]在下面已经更新了,不需要跳很多次。 29 if (p[i] != 0) p[i] = j; // 类似于记忆化,前缀i的跳的最优位置。 30 ans += i - j; 31 } 32 cout << ans; 33 return 0; 34 }