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 }
View Code

还有一种枚举方法是扫描所有可能的中缀末位,取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 }
View Code

第三种方法是用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 }
View Code

 

以上是关于CF 126B KMP/Z_match的主要内容,如果未能解决你的问题,请参考以下文章

CF126B PasswordKMPBy cellur925

Codeforces 126B(kmp)

126B Password[扩展kmp学习]

codeforces 126B

CodeForces - 126B Password

CodeForces - 126B Password