KMP
KMP模板
void kmp(string s1,string s2){
string s = ' ' + s1 + '#' + s2;
int n = s1.size(), m = s.size();
vector <int> pi{0,0};
for(int i = 2,j = 0;i <= n;i++){
while(j > 0 && s[i] != s[j+1]) j = pi[j];
if(s[i] == s[j+1]){
pi.push_back(j+1);
}
else pi.push_back(0);
j = pi[i];
}
pi.push_back(0);
for(int i = n+2,j;i<m;i++){
j = pi[i-1];
while(j > 0 && s[i] != s[j+1]) j = pi[j];
if(s[i] == s[j+1]) pi.push_back(j+1);
else pi.push_back(0);
if(pi[i] == n){
for(int k=i-2*n;k<i-n;k++) flag[k] = true;
}
}
return;
}
用法一:最长前缀
即求一个字符串的最长前缀,可以引申到求模板串s1在文本s2中的重复次数。
题目:[https://www.luogu.com.cn/problem/P1470](P1470 最长前缀)
题目意思是:给出字符串集合P,和一个长字符串s,判断s的最长前缀s1,使这个前缀s1可以分解为P中的字符串。
可以通过 KMP+暴力 的做法解题:
对于P中每一个字符串t做KMP操作
1、形成 S = '&' + t + '#' + s 的字符串
2、对于字符串S,可以用KMP求最长前缀的方式得到pi值
3、当pi[i] == t.size()时,遍历这部分字串 flag[k] = true 表示标记这部分可以分解。
最后遍历s,找到第一个没有被标记的位置,即得到了最长前缀。
while(cin>>t){
s = s + t;
}
此题易错点:
题目输入的s可能会有多行,需要循环代码并连接得到最终s
int main(){
vector <string> vs;
string s="",t;
cin>>t;
while(t != "."){
vs.push_back(t);
cin>>t;
}
while(cin>>t){
s = s + t;
}
for(string t:vs){
kmp(t,s);
}
for(int i=1;i<=s.size();i++){
if(!flag[i]){
cout<<i-1<<endl;
return 0;
}
}
cout<<s.size()<<endl;
return 0;
}
用法二:压缩字串
寻找一个最短的字符串 t,使得 s 可以被 t 的一份或多份拷贝的拼接表示
题目:https://www.luogu.com.cn/problem/P4391
题目就是需要我们对给定的字符串s,找到它的最短长度s2,满足s是s2不断自我连接得到的字串。
我们直接对字符串s进行KMP操作
得到的s2长度就是n-pi[n]
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
string s;
vector <int> pi{0,0};
cin>>n>>s;
s = '&' + s;
for(int i=2,j=0;i<=n;i++){
while(j > 0 && s[j+1] != s[i]) j = pi[j];
if(s[i] == s[j+1]) pi.push_back(j+1);
else pi.push_back(0);
j = pi[i];
}
cout<<n-pi[n]<<endl;
return 0;
}

浙公网安备 33010602011771号