【CF126B】password【KMP】解题报告

题目描述:

Asterix, Obelix and their temporary buddies Suffix and Prefix has finally found the Harmony temple. However, its doors were firmly locked and even Obelix had no luck opening them.

A little later they found a string s, carved on a rock below the temple's gates. Asterix supposed that that's the password that opens the temple and read the string aloud. However, nothing happened. Then Asterix supposed that a password is some substring t of the string s.

Prefix supposed that the substring t is the beginning of the string s; Suffix supposed that the substring t should be the end of the string s; and Obelix supposed that t should be located somewhere inside the string s, that is, t is neither its beginning, nor its end.

Asterix chose the substring t so as to please all his companions. Besides, from all acceptable variants Asterix chose the longest one (as Asterix loves long strings). When Asterix read the substring t aloud, the temple doors opened.

You know the string s. Find the substring t or determine that such substring does not exist and all that's been written above is just a nice legend.

题意翻译:

Asterix,Obelix和他们的临时伙伴Suffix、Prefix已经最终找到了和谐寺。然而和谐寺大门紧闭,就连Obelix的运气也没好到能打开它。

不久他们发现了一个字符串S(|S|<=1000000),刻在和谐寺大门下面的岩石上。Asterix猜想那一定是打开寺庙大门的密码,于是就大声将字符串朗读了出来,然而并没有什么事发生。于是Asterix又猜想密码一定是字符串S的子串T。

Prefix认为T是S的前缀,Suffix认为T是S的后缀,Obelix却认为T应该是S中的某一部分,也就是说,T既不是S的前缀,也不是S的后缀。

Asterix选择子串T来满足所有伙伴们的想法。同时,在所有可以被接受的子串变形中,Asterix选择了最长的一个(因为Asterix喜欢长的字符串)当Asterix大声读出子串T时,寺庙的大门开了。 (也就是说,你需要找到既是S的前缀又是S的后缀同时又在S中间出现过的最长子串)

现在给你字符串S,你需要找到满足上述要求的子串T。

输入格式:

You are given the string ss whose length can vary from 11 to 10^{6}106 (inclusive), consisting of small Latin letters.

一个长度在[1,1000000]间的只包含小写字母的字符串S。

输出格式:

Print the string tt . If a suitable tt string does not exist, then print "Just a legend" without the quotes.

输出子串T,如果T不存在,输出 "Just a legend",不包含引号。

题解:

首先一看到这道题,要判断字符串是否出现,最先想到kmp,但是单纯用kmp去匹配,是O(N^2)的时间复杂度,但是数据量却有1000000,肯定不行。

那么就得启发我们换换思路,是否一定要匹配呢?

换个角度思考,抛开字符串要在中间出现不谈,既然题目要求最长长度的前后缀,kmp的next数组不就是如此吗?

所以如果next[n]对应的字符串中间出现过,答案肯定就是。那么如何判断呢?如图所示:

如果2~n-1(为何不是1-n,因为题目中说了此串的第三次出现不能在前后缀位置)当中有串和1-next[n]相同,则意味着如果缩短1-i的字符串,把后几位扔掉,总会存在2~n-1的一个i,它的后缀与1~next[n]相同,但是1~next[n]又是1~i的前缀,所以这就是前后缀相同了,只要判断next[i]==next[n]就行了

如果next[n]没有出现过3次,那么1~next[n]的最长相同前后缀一定是答案(因为它长度最大,而且分别在1-next[n]、n-next[n]+1~next[n]出现了2次,总共4次,满足条件)

29行代码搞定

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n;
 4 char s[1000001],s1[1000001];
 5 int p[1000001],len,p1[1000001];
 6 int main(){
 7     int i;
 8     scanf("%s",s+1);
 9     n=strlen(s+1);
10     int j=0;
11     for(i=2;i<=n;i++){
12         while(j&&s[i]!=s[j+1])j=p[j];
13         if(s[i]==s[j+1])j++;
14         p[i]=j;
15     }
16     int x=p[n],y=n-p[n]+1;
17     for(i=2;i<=n-1;i++)if(p[i]==p[n])len=max(len,p[n]);
18     for(i=y;i<=n;i++)s1[i-y+1]=s[i];
19     j=0;
20     for(i=2;i<=p[n];i++){
21         while(j&&s1[i]!=s1[j+1])j=p1[j];
22         if(s1[i]==s1[j+1])j++;
23         p1[i]=j;
24     }
25     if(p1[p[n]])len=max(len,p1[p[n]]);
26     if(len)for(i=1;i<=len;i++)putchar(s[i]);
27     else puts("Just a legend");
28     return 0;
29 } 

猜你喜欢

转载自www.cnblogs.com/zbsakioi/p/10813798.html