1. 程式人生 > >1511: [POI2006]OKR-Periods of Words

1511: [POI2006]OKR-Periods of Words

style pro spa 最優 scanf read long 包含 pre

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 }

1511: [POI2006]OKR-Periods of Words