KMP复习
kmp真的是一个好算法,将暴力\(O(nm)\)的匹配改进成了\(O(n+m)\)。
nb!
推荐一个视频。
#include
using std::cin;
using std::cout;
using std::endl;
using std::string;
const int maxn = 1000005;
string s1, s2;
int next[maxn];
int main() {
std::ios::sync_with_stdio(false);
cin >> s1 >> s2;
next[0] = 0;
for(int i = 1, j = 0; i < s2.size(); i++) {
while(j > 0 && s2[j] != s2[i]) j = next[j - 1];
if(s2[j] == s2[i]) j++;
next[i] = j;
}
for(int i = 0, j = 0; i < s1.size(); i++) {
while(j > 0 && s2[j] != s1[i]) j = next[j - 1];
if(s2[j] == s1[i]) j++;
if(j == s2.size()) {
cout << i - j + 2 << endl;
}
}
for(int i = 0; i < s2.size(); i++) {
cout << next[i] << ' ';
}
cout << endl;
}
这里是一道加深理解的kmp题:CF126B Password
题意:给你字符串,找到最长的既是前缀也是后缀还在中间出现过的子串长度。
这道题只需要用到kmp算法的next数组就能解决。
首先跑一遍kmp,如果整个字符串的next为0,就没有答案。
然后暴力地找最大前缀之后是否有next能大于等于其长度的,如果有就说明确实有满足题意的最长子串。(前缀与中间串是可以重叠的)
如果找不到,还可能有其他的答案,我们缩小至最长前缀的next值大小。如果不等于0,那就是答案,否则无解。
这道题告诉我们kmp中的next数组的含义非常丰富,加以利用可以线性地解决问题!
#include
using std::cin;
using std::cout;
using std::endl;
using std::string;
const int maxn = 1e6 + 5;
string str;
int next[maxn];
int main() {
cin >> str;
next[0] = 0;
for(int i = 1, j = 0; i < str.size(); i++) {
while(j > 0 && str[i] != str[j]) j = next[j - 1];
if(str[i] == str[j]) j++;
next[i] = j;
}
int k = next[str.size() - 1];
if(k == 0) {
puts("Just a legend");
return 0;
}
for(int i = k; i < str.size() - 1; i++) {
if(next[i] >= k) {
for(int i = 0; i < k; i++) cout << str[i];
return 0;
}
}
if(next[k - 1] == 0) {
puts("Just a legend");
return 0;
}
for(int i = 0; i < next[k - 1]; i++) {
cout << str[i];
}
return 0;
}