字符串的问题
题目
有一个字符串 让你找到这个字符串 S 里面的子串T 这个子串 T 必须满足即使这个串的前缀 也是这个串的后缀 并且 在字符串中也出现过一次的(提示:要求满足前后缀的同时也要在字符串中出现一次 只是前后缀可不行 输出最长满足要求字符串)
输入:
给出一个字符串 长度 1 到 1e6 全部是小写字母
输出:
如果找的到就输出这个子串T 如果不行就输出 Just a legend
示例1
输入
fixprefixsuffix
输出
fix
示例2
输入
abcdabc
输出
Just a legend
转载于:代码来源here
思路:
1.KMP算法的next数组储存的是next[j]:
(1)前j-1个数的最长相同前后缀长度
(2)字符不匹配时,应该回溯到的字符的下标
2.next[s.length()]中存的是字符串s的最长相同前后缀长度
3.重点:我们要如何找中间有没有和相同前后缀一样的字符串?
(1)给中间的字符串i∈[1,s.length())的next数组的值进行标记,vis[next[i]]=1,如果,vis[next[s.length()]]==1,则说明中间还出现过一次长度为next[s.length()]的字符串 (vis数组标记的是中间有没有出现过(存的是next数组[1,len)的值))
eg:s字符串
abcabcab
next下标 前缀 next值(最长相同前后缀)往后挪了一个
0 ---------- a ---------- -1
1 ---------- ab ---------- 0
2 ---------- abc ---------- 0
3 ---------- abca ---------- 0
4 ---------- abcab ---------- 1
5 ---------- abcabc ---------- 2
6 ---------- abcabca ---------- 3
7 ---------- abcabcab ---------- 1
8----------- s.length() ------------2
因为next[s.length.()]=2,vis[2]=1,说明中间有相同的后缀可以和前缀匹配,所以满足要求
(2)否则,让 len=next[len] (len=s.length()) ,去查看开头长度为next[len]的字串有没有循环节,因为前后缀相同,若果前缀有的话,后缀也有,如果在中间查找时找到了字符串的第一个字符即前缀的位置,则说明没有。
eg: aaa
next下标 前缀 next值 vis
0 ---------- a ---------- -1--------0
1 ---------- aa ---------- 0--------1
2 ---------- aaa ---------- 1--------1
3 ---------- s.length()---------- 2--------0
vis[2]==0, 2!=0,进入一轮循环,x=next[2]=1 循环节为1
vis[1]==1 成立 输出长度为1的前缀
eg:ab
next下标 前缀 next值 vis
0 ---------- a ---------- 1--------0
1 ---------- ab ---------- 0--------1
2 ---------- s.length()---------- 0--------0
vis[0]=1,x=0,不进入循环,输出Just a legend
#include<iostream>
#include<string.h>
using namespace std;
const int maxn=1e6+5;
int Next[maxn];
int vis[maxn];
void prefix(string s){
int j,k;
j=0,k=-1;
Next[0]=-1;
int lens=s.length();
while(j<lens){//Next [0,s.length()],Next[s.length()]存的是这个字符串的最长公共前后缀
if(k==-1||s[k]==s[j]){
j++;
k++;
Next[j]=k;
}
else{
k=Next[k];
}
}
// for(int i=0;i<=lens;i++){
// cout<<"next:"<<Next[i]<<endl;
// }
}
int main(){
string s;
cin>>s;
memset(vis,0,sizeof(vis));
int len=s.length();
prefix(s);
for(int i=1;i<len;i++){
vis[Next[i]]=1;//vis标记中间有没有出现过
}
int x=Next[len];//如果vis[len]=1,说明中间出现过,此时就是答案
// for(int i=0;i<=len;i++){
// cout<<"vis"<<i<<":"<<vis[i]<<endl;
// }
while(!vis[x]&&x!=0){//vis标记是否中间出现过,若出现过直接跳出,否则继续找循环节,若x=0,说明找到了字符串的第一个字符,说明此时没有匹配的答案
x=Next[x];
}
if(x==0){
cout<<"Just a legend"<<endl;
}
else{
for(int i=0;i<x;i++){
cout<<s[i];
}
cout<<endl;
}
return 0;
}
(其实,我也还有点懵!!!)