CF 126B KMP/Z_match
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF 126B KMP/Z_match相关的知识,希望对你有一定的参考价值。
大意:给一个字符串,问最长的既是前缀又是后缀又是中缀(这里指在内部出现)的子串。
我自己的做法是用KMP的next数组,对所有既是前缀又是中缀的位置计数,再从next[n]开始走next,也即枚举所有既是前缀又是后缀的子串,看cnt[i]是否大于0,如果大于0则说明作为中缀出现过(也即有大于i的某个位置的next为i)
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <stdio.h> 7 #include <math.h> 8 #include <stdlib.h> 9 #include <queue> 10 #include <stack> 11 #include <map> 12 #include <set> 13 14 using namespace std; 15 16 const int N=1e6+12345; 17 char s[N]; 18 int next[N]; 19 int cnt[N]; 20 void getNext(char *word,int n=0){ 21 n=(n==0)?strlen(word):n; 22 memset(next,0,sizeof(next)); 23 int i,j; 24 for (i=1;i<n;i++){ 25 j=i; 26 while (j){ 27 j=next[j]; 28 if (word[i]==word[j]){ 29 next[i+1]=j+1; 30 break; 31 } 32 } 33 } 34 } 35 int main () { 36 scanf("%s",s); 37 int n=strlen(s); 38 getNext(s,n); 39 for (int i=2;i<n;i++) 40 cnt[next[i]]++; 41 if (next[n]==0) 42 puts("Just a legend"); 43 else { 44 for (int i=next[n];i;i=next[i]) { 45 if (cnt[i]) { 46 for (int j=0;j<i;j++) 47 printf("%c",s[j]); 48 puts(""); 49 return 0; 50 } 51 } 52 puts("Just a legend"); 53 } 54 return 0; 55 }
还有一种枚举方法是扫描所有可能的中缀末位,取next[i]的最大值,也即求出最大的既是前缀又是中缀的子串,再从next[n]开始走next,第一次走到0<next[i]<=len时,也即找到了子串。
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <stdio.h> 7 #include <math.h> 8 #include <stdlib.h> 9 #include <queue> 10 #include <stack> 11 #include <map> 12 #include <set> 13 14 using namespace std; 15 16 const int N=1e6+12345; 17 char s[N]; 18 int next[N]; 19 void getNext(char *word,int n=0){ 20 n=(n==0)?strlen(word):n; 21 memset(next,0,sizeof(next)); 22 int i,j; 23 for (i=1;i<n;i++){ 24 j=i; 25 while (j){ 26 j=next[j]; 27 if (word[i]==word[j]){ 28 next[i+1]=j+1; 29 break; 30 } 31 } 32 } 33 } 34 int main () { 35 scanf("%s",s); 36 int n=strlen(s); 37 getNext(s,n); 38 int len=0; 39 for (int i=2;i<n;i++) 40 len=max(len,next[i]); 41 for (int i=next[n];i>0;i=next[i]) { 42 if (i>len) continue; 43 for (int j=0;j<i;j++) 44 printf("%c",s[j]); 45 puts(""); 46 return 0; 47 } 48 puts("Just a legend"); 49 return 0; 50 }
第三种方法是用z算法,也就是算出每个位置与本串的最长公共前缀。再枚举所有后缀,check当前后缀是否也是前缀,如果是的话,则看之前是否有z[i]也就是与本串的LCP大于当前后缀长度,如果有,则当前后缀也一定可以作为中缀出现,当前后缀是最长的符合题意的子串。
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <stdio.h> 7 #include <math.h> 8 #include <stdlib.h> 9 #include <queue> 10 #include <stack> 11 #include <map> 12 #include <set> 13 14 using namespace std; 15 16 const int N=1e6+12345; 17 char s[N]; 18 int z[N]; 19 void Z_match(char *s,int n=0) { 20 n=(n==0)?strlen(s):n; 21 z[0]=n; 22 int l=0,r=0; 23 for (int i=1;i<n;i++) { 24 if (i>r) { 25 l=i,r=i; 26 while (r<n&&s[r-i]==s[r]) r++; 27 z[i]=r-l; 28 r--; 29 } 30 else { 31 int k=i-l; 32 if (z[k]<r-i+1) 33 z[i]=z[k]; 34 else { 35 l=i; 36 while (r<n&&s[r-i]==s[r]) r++; 37 z[i]=r-l; 38 r--; 39 } 40 } 41 } 42 } 43 int main () { 44 scanf("%s",s); 45 int n=strlen(s); 46 Z_match(s,n); 47 int ret=0,maxZ=0; 48 for (int i=1;i<n;i++) { 49 if (z[i]==n-i) { 50 if (maxZ>=n-i) { 51 ret=n-i; 52 break; 53 } 54 } 55 maxZ=max(maxZ,z[i]); 56 } 57 if (ret==0) 58 puts("Just a legend"); 59 else { 60 for (int i=0;i<ret;i++) 61 printf("%c",s[i]); 62 puts(""); 63 } 64 return 0; 65 }
以上是关于CF 126B KMP/Z_match的主要内容,如果未能解决你的问题,请参考以下文章