KMP好就没用,忘了,再练习一下下
题目描述
帕秋莉掌握了一种水属性魔法
这种魔法可以净化黑暗
帕秋莉发现对于一个黑暗的咒语s,可以使用这个水元素魔法净化它,净化的咒语是一个最长的字符串t,t满足以下条件:
它是s的前缀
它是s的后缀
除前缀和后缀外,它还在s中出现过至少一次
既然你都学会了,那么净化的工作就交给你了!
输入描述:
一行字符串 s ,代表黑暗咒语
输出描述:
一个字符串 t ,表示满足条件的最长净化咒语
输入
tqrwantoacthisproblembutqristooweaktodoitqr
输出
tqr
三个要求:
- 既是前缀又是后缀
- 在字符串中出现过(不是前缀也不是后缀的出现,也就是说在字符串中出现至少三次)
- 最长
首先回想一下最长前缀后缀怎么求,很简单嘛,kmp 的 next 数组保存的不就是以当前为结尾的从头开始的字符串和最后的后缀匹配的最长长度,那么第一个条件好处理了,每次取 next 数组里的长度截取字符串即可
在字符串中出现过也就是说kmp的时候匹配次数大于 2 即可,这样的处理方式是在匹配成功后计数器加 1 ,然后当作没有匹配成功来处理,即执行 k = next[ k ]
最长更好处理,next[ len ] (len 是字符串长度)保存的是整个字符串中最长的前后缀相等的,只需要从next[ len ]开始计算,如果不符合条件,k = next [ k ],一旦符合,必定是最长的
CODE
#include <bits/stdc++.h>
using namespace std;
const int N = 100100;
int nex[N];
void get_next(char *p){
int len = strlen(p);
nex[0] = -1;
int i = 0, k = -1;
while (i < len){
if(k == -1 || p[i] == p[k]){
i++;
k++;
nex[i] = k;
}
else k = nex[k];
}
}
int net[N];
int kmp(char *a, char *b){
int lena = strlen(a);
int lenb = strlen(b);
int i = 0, k = 0;
int res = 0;
while (i < lena && k < lenb){
if(k == -1 || a[i] == b[k]){
++i, ++k;
}
else k = nex[k];
if(k == lenb){
k = nex[k];
res++;
}
}
return res;
}
char s[N];
int main()
{
scanf("%s", s);
get_next(s);
int k = nex[strlen(s)];
int res = 0;
char t[N];
while (k != -1){
int i;
for (i = 0; i < k; i++)t[i] = s[i];
t[i] = '\0';
res = kmp(s, t);
if(res >= 3)break;
k = nex[k];
}
printf("%s\n", t);
return 0;
}
我们若不坚强,就必将灭亡!