您的位置:首页 > 其它

[DBSDFZOJ 多校联训] Password

2017-08-07 17:00 176 查看

Password

password.in/.out

描述

你来到了一个庙前,庙牌上有一个仅包含小写字母的字符串 s。

传说打开庙门的密码是这个字符串的一个子串 t,并且 t 既是 s 的前缀又是 s 的后缀并且还在 s 的中间位置出现过一次。

如果存在这样的串,请你输出这个串,如有多个满足条件的串,输出最长的那一个。

如果不存在这样的串,输出"Just a legend"(去掉引号)。

输入格式

仅一行,字符串 s。

输出格式

如题所述

样例输入

fixprefixsuffix

样例输出:

fix

数据范围

对于 60%的数据, s 的长度<=100

对于 100%的数据, s 的长度<=100000


看到"同时是前缀与后缀的串的长度"可能大家都能想到 $KMP$ 算法w

但是题目对于这个串有一定限制: 必须在中间出现过至少一次. 所以我们计算出这个串的长度之后还要拿着这个串进原串匹配一发, 如果匹配次数达到 $3$ 或以上就说明这个串除了在两端出现过之外还在中间出现了至少一次. 如果没有在中间出现则在刚刚验证失败的子串内接着找同时是这个串前缀与后缀的串的最大长度.

也就是说先求 $KMP$ 中的失配边, 然后在不符合题意的时候一直跳失配边就好了.

跳到最后还是没有匹配到说明无解.

(考试的时候输入数据神特么文件尾没有空行(╯‵□′)╯︵┻━┻逐字符读入直接死循环然后本来能A的题...裱死出数据的家伙)

参考代码:

GitHub

#include <cctype>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

const int MAXN=100010;

int len;
int fail[MAXN];
char buffer[MAXN];

int Read(char*);
void Print(char*,int);
void KMP(char*,int*,int);
int Match(char*,int,char*,int,int*);

int main(){
scanf("%s",buffer);
len=strlen(buffer);
KMP(buffer,fail,len);
for(int k=fail[len];k>0;k=fail[k]){
if(Match(buffer,len,buffer,k,fail)>2){
Print(buffer,k);
return 0;
}
}
puts("Just a legend");
return 0;
}

void KMP(char* buf,int* f,int len){
int j=0,k=-1;
f[0]=-1;
while(j<len){
if(k==-1||buf[j]==buf[k]){
f[++j]=++k;
}
else k=f[k];
}
}

int Match(char* buf,int lb,char* ptn,int lp,int* f){
int j=0,ans=0;
for(int i=0;i<lb;i++){
while(j>0&&buf[i]!=ptn[j])
j=f[j];
if(buf[i]==ptn[j])
j++;
if(j==lp){
ans++;
j=f[j];
}
}
return ans;
}

inline void Print(char* buf,int len){
for(int i=0;i<len;i++){
putchar(buf[i]);
}
putchar('\n');
}

inline int Read(char* buf){
int pos=0;
do{
buf[pos]=getchar();
}while(isspace(buf[pos]));
while(!isspace(buf[pos])&&buf[pos]!=EOF){
buf[++pos]=getchar();
}
buf[pos]='\0';
return pos;
}


Backup

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: