zoukankan      html  css  js  c++  java
  • CF #93 div1 B. Password KMP/Z

    题目链接:http://codeforces.com/problemset/problem/126/B

    大意:给一个字符串,问最长的既是前缀又是后缀又是中缀(这里指在内部出现)的子串。

    我自己的做法是用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
  • 相关阅读:
    Windows server 2016 解决“无法完成域加入,原因是试图加入的域的SID与本计算机的SID相同。”
    Windows Server 2016 辅助域控制器搭建
    Windows Server 2016 主域控制器搭建
    Net Framework 4.7.2 覆盖 Net Framework 4.5 解决办法
    SQL SERVER 2012更改默认的端口号为1772
    Windows下彻底卸载删除SQL Serever2012
    在Windows Server2016中安装SQL Server2016
    SQL Server 创建索引
    C#控制台或应用程序中两个多个Main()方法的设置
    Icon cache rebuilding with Delphi(Delphi 清除Windows 图标缓存源代码)
  • 原文地址:https://www.cnblogs.com/micrari/p/5232576.html
Copyright © 2011-2022 走看看