1511: [POI2006]OKR-Periods of Words

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 }

猜你喜欢

转载自www.cnblogs.com/mjtcn/p/9350964.html