在阅读这篇文章之前,我们假定你已经掌握了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里最大的,取总体最小,是一个答案,找到所有答案里长度最长的字典序最小的,就是答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| #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[]){ getNext(strp,nextt); ll pl=strp.size(),sl=strs.size(); ll fir=0,las=-1,mnl=min(sl,pl); while(las<mnl-1&&strp[las+1]==strs[las+1]){ las++; } 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() { 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); for(int j=1;j<=n;j++){ ll mx=0; exKMP(cur,skr[j],nex,ext);
for(int k=0;k<skr[j].size();k++){ mx=max(mx,ext[k]); } mn=min(mn,mx); } 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; }
|