扩展KMP(讲解+模版+例题)
在阅读这篇文章之前,我们假定你已经掌握了KMP:n+1次探里的定义。
引入:扩展KMP是干什么的
扩展KMP解决的是源串S的每一个后缀与模式串P的最长公共前缀的长度的问题,并求解出答案extend数组,例如,ababac与aba的extend数组是3 0 3 0 1 0,这里extend[i]表示s[i:5](i从0开始)与p[0:2]的最长公共前缀的长度。
next数组的定义
这里的next数组与KMP里的不同。
next[i]表示从i开始的p的后缀与p的最长公共前缀的长度,也就是,p对p求扩展KMP,可以参见2019 Multi-University Training Contest 5 - 1006 - string matching。
我们先假设已经有了next数组,来求extend,因为next数组的求法是和extend一样的。
扩展KMP
递推:已知extend[i-1],如何求extend[i]?
我们假设在前面匹配时,向右匹配到的最远坐标为last,是从first开始匹配的,也就是说s[first:last]=p[0:last-first]。可以推出s[i:last]=p[i-first:last-first],但这个不是和p的开头匹配,还不能用,我们取extend[i]=min(last-i+1, next[i-first]),看看p[i-first:last-first]和p开头有多少相同。然后向后检测extend[i]能不能更大,这一块暴力,别忘了最后更新first和last。
初始:暴力大法好
暴力检测s和p最大公共前缀长度extend[0]。
求next数组
和上面一样。next的0位置必定是p的长度,代码中last初值设为0是为了避免初始化。
例题
给一堆字符串,求最长公共字串。
找一个最短的串,暴力求出每一个后缀,和所有串匹配,找到每个extend里最大的,取总体最小,是一个答案,找到所有答案里长度最长的字典序最小的,就是答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
#include<iostream>
#define ll long long
#define db double
#define ioss ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
int n,cnt;
ll ext[220],nex[220];
string skr[4020];
string ans[4020];
void getNext(string &strp,ll nextt[]){
ll pl=strp.size();
ll fir=0,las=0;
nextt[0]=pl;
for(ll i=1;i<pl;i++) {
nextt[i] = min(las - i + 1, nextt[i - fir]);
if (nextt[i] < 0) nextt[i] = 0;
while (i+nextt[i]<pl && strp[nextt[i]] == strp[i + nextt[i]]) {
nextt[i]++;
}
if (i + nextt[i] - 1 > las) {
las = i + nextt[i] - 1;
fir = i;
}
}
}
void exKMP(string &strp,string &strs,ll nextt[],ll extt[]){
//cout<<"start exKMP:"<<endl;
getNext(strp,nextt);
ll pl=strp.size(),sl=strs.size();
ll fir=0,las=-1,mnl=min(sl,pl);
//cout<<strp<<endl<<strs<<endl;
while(las<mnl-1&&strp[las+1]==strs[las+1]){
las++;
//cout<<"init++"<<endl;
}
extt[0]=las+1;
for(ll i=1;i<sl;i++){
extt[i]=min(las-i+1,nextt[i-fir]);
if(extt[i]<0) extt[i]=0;
while(extt[i]<pl && i+extt[i]<sl && strp[extt[i]]==strs[i+extt[i]]){
extt[i]++;
}
if(i+extt[i]-1>las){
las=i+extt[i]-1;
fir=i;
}
}
}
int main() {
//ioss;
//freopen("1.in","r",stdin);
//freopen("2.out","w",stdout);
while(scanf("%d",&n)==1&&n){
cnt=0;
int mnlen=300,mnlenx;
for(int i=1;i<=n;i++) {
cin >> skr[i];
if (skr[i].size() < mnlen) {
mnlen = skr[i].size();
mnlenx = i;
}
}
for(int i=0;i<skr[mnlenx].size();i++){
ll mn=1e10;
string cur=skr[mnlenx].substr(i);
//out<<i+1<<": cur= "<<cur<<endl;
for(int j=1;j<=n;j++){
ll mx=0;
exKMP(cur,skr[j],nex,ext);
/*cout<<"nex: ";
for(int k=0;k<cur.size();k++){
cout<<nex[k]<<' ';
}
cout<<endl;
cout<<"ext: ";*/
for(int k=0;k<skr[j].size();k++){
//cout<<ext[k]<<' ';
mx=max(mx,ext[k]);
}
//cout<<endl;
mn=min(mn,mx);
//cout<<"mn = "<<mn<<endl;
}
if(mn>0){
if(cnt==0||(mn==ans[1].size())){
ans[++cnt]=cur.substr(0,mn);
}
else if(mn>ans[1].size()){
cnt=0;
ans[++cnt]=cur.substr(0,mn);
}
}
}
if(cnt){
sort(ans+1,ans+1+cnt);
cout<<ans[1]<<endl;
}
else cout<<"IDENTITY LOST"<<endl;
}
return 0;
}