Hdu--4333(扩展KMP)
2014-12-17 01:34:48
思路:仔细思考,发现如果用扩展KMP,求出每个位置到末尾的最大前缀匹配长度,比如1~100,位置50能匹配到75,那么当我们把50~100移动到开头。前25个字符是一样的,需要比较的是第26个字符(也就是移动前的第76个字符),所以如果设next[i]为位置 i 到末尾的最大前缀匹配长度,需要比较的就是s[i + next[i]]与s[next[i]],我们发现如果i + next[i] < len,那么s[i + next[i]] 与 s[next[i]]必然不同,但如果i + next[i] == len,则需要不断地比较i + next[i] 与 next[i]、i + next[i] + 1 与 next[i] + 1。。。。知道找到不同,这显然效率不够。我们可以把原先的串先复制一倍长度,这样就可以把“不断比较的过程省去”。
如果next[i] >= len,说明移动后相等,如果s[i + next[i]] > s[next[i]] 说明移动后数更大,如果s[i + next[i]] < s[next[i]] 说明移动后数更小 、
最后还要注意:题目中说数字不能重复,所以要用KMP求出最大循环节,以去重(具体求最大循环节方法见代码)
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <iostream> 5 using namespace std; 6 const int maxn = 200010; 7 8 int T,len,ans1,ans2,ans3; 9 int next[maxn],P[maxn]; 10 char s[maxn]; 11 12 void Get_P(){ 13 P[0] = -1; 14 int j = -1; 15 for(int i = 1; i < len; ++i){ 16 while(j > -1 && s[j + 1] != s[i]) j = P[j]; 17 if(s[j + 1] == s[i]) j++; 18 P[i] = j; 19 } 20 } 21 22 void Ex_kmp(){ 23 int k = 0,j,p,L; 24 next[0] = len; 25 while(k < len - 1 && s[k + 1] == s[k]) k++; 26 next[1] = k; 27 k = 1; 28 int top = len / 2; 29 for(int i = 2; i < top; ++i){ 30 p = k + next[k] - 1,L = next[i - k]; 31 if(i + L > p){ 32 j = p - i + 1; 33 if(j < 0) j = 0; 34 while(i + j < len && s[i + j] == s[j]) j++; 35 next[k = i] = j; 36 } 37 else next[i] = L; 38 } 39 } 40 41 int main(){ 42 scanf("%d",&T); 43 for(int tt = 1; tt <= T; ++tt){ 44 scanf("%s",s); 45 len = strlen(s); 46 Get_P(); 47 int t = len - P[len - 1] - 1; 48 len = t + len % t; //用KMP求最大循环节 49 for(int i = 0; i < len; ++i) s[i + len] = s[i]; 50 len *= 2; 51 Ex_kmp(); 52 int top = len / 2; 53 ans1 = ans2 = ans3 = 0; 54 for(int i = 0; i < top; ++i){ 55 if(next[i] >= len / 2) ans2++; 56 else if(s[i + next[i]] > s[next[i]]) ans3++; 57 else ans1++; 58 } 59 printf("Case %d: %d %d %d\n",tt,ans1,ans2,ans3); 60 } 61 return 0; 62 }