扩展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是为了避免初始化。

例题

hdu2328

给一堆字符串,求最长公共字串。

找一个最短的串,暴力求出每一个后缀,和所有串匹配,找到每个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;
}
posted @ 2019-08-10 13:48  CCWUCMCTS  阅读(1180)  评论(0编辑  收藏  举报