[学习笔记]扩展KMP
〇、前言 ¶
扩展 \(\rm kmp\) 主要的功能是:对于串 \(S,T\),记 \(ext_i=\text{LCP}(S[i:],T)\),它可以在线性复杂度内求得所有 \(ext_i(i\in [1,|S|])\).
不难看出,若某个 \(ext_i=|T|\),那么说明 \(T=S[i:i+|T|-1]\),事实上,这就是 \(S,T\) 的一个匹配位置。所以说,它叫做扩展 \(\rm KMP\). 并且,其思想是基于 \(\rm KMP\) 的,故而,\(\rm KMP\) 是必要的前置知识。
注意:下文所有的字符串的首下标都是 \(1\) 而非 \(0\).
壹、算法过程 ¶
事实上,扩展 \(\rm KMP\) 也是利用了 \(\rm KMP\) 的 “利用已经之前部分匹配的有效信息,保持主串指针不回溯,通过修改指针,让模式串尽量地移动到有效的位置。”
而 \(\rm exkmp\) 主要利用两个数组:\(ext_i,Z_i\),\(ext_i\) 的定义上文给出,对于 \(Z_i\),有
即模式串的每个后缀与模式串本身的匹配,不难看出,\(Z_i\) 的本质还是 \(ext_i\),所以我们只需要搞清楚如何求解 \(ext_i\),即可解决 \(Z_i\).
进入算法过程,假设我们当前要求得 \(ext_i\),已经算出 \(ext_1,ext_2,\cdots ext_{i-1}\),并且有数对 \((p,r)\),其中 \(p\) 是使得 \(p+ext_p-1\) 最大的 \(p\),并且记 \(r=p+ext_p-1\),\(r\) 的含义即当前最长的 \(S[p:],T\) 的匹配的右边界. 我们假设已知 \(Z\) 数组。
由定义,我们已知 \(S[p:r]=T[1:r-p+1]\),从而,有 \(S[i:r]=T[i-p+1:r-p+1]\),所以,若我们要匹配 \(S[i:]\) 与 \(T\),我们可以先匹配 \(S[i:r],T\),即先匹配 \(T[i-p+1,r-p+1]\) 与 \(T\),针对这个匹配,我们就可以使用 \(Z\) 数组了。
记 \(len=Z_{i-p+1}\),那么,分两种情况:
- 若 \(i-p+1+len-1<r-p+1\Rightarrow i+len-1<r\),则说明 \(T[i-p+1:r-p+1]\) 与 \(T\) 存在某个字符不一样,那么一定有 \(ext_i=len\);
- 若 \(i-p+1+len-1\ge r-p+1\Rightarrow i+len-1\ge r\),那么可以说明 \(ext_i=len\) 吗?不行,因为我们目前只知道的信息是,\(T[i-p+1:r-p+1]\) 为 \(S[i:]\) 的一个前缀,同时 \(T[i-p+1:r-p+1]\) 也是 \(T\) 的一个前缀,对于 \(T\) 超过 \(r-p+1\) 的部分与 \(S\) 的匹配我们是尚不知晓的,换句话说,我们只知道 \(ext_i\) 至少为 \(len\),至于它到底有多大,我们得将 \(S[r+1:]\) 与 \(T[r-p+1+1:]\) 进行暴力匹配(\(S[r]=T[r-p+1]\) 我们也是知道的),并且此时我们可以更新 \((p,r)\);
至于如何计算 \(Z\),由于我们计算 \(Z_i\) 时 \(Z_1,Z_2,\cdots Z_{i-1}\) 都是知道的,所以也可以直接使用上述过程,它是天然自洽的。
特别地,若 \(i>r\),那么我们直接暴力匹配,得到新的 \(r,p\) 即可,不过需要注意的是,如果一开始就失配,需要特判,当然也有可能是我打法问题 🙃.
另外,我尝试在代码实现中以全闭区间为下标,这样确实更容易理解......不过,代码实现难上加难了......以后要打这个东西,还是下标从 \(0\) 开始,打左闭右开区间算了......
叁、参考代码 ¶
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
// #define NDEBUG
#include<cassert>
namespace Elaina{
#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define mmset(a, b) memset(a, b, sizeof a)
// #define int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
template<class T>inline T readin(T x){
x=0; int f=0; char c;
while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
return f? -x: x;
}
template<class T>inline void writc(T x, char s='\n'){
static int fwri_sta[1005], fwri_ed=0;
if(x<0) putchar('-'), x=-x;
do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
putchar(s);
}
}
using namespace Elaina;
const int maxn=2e7;
char a[maxn+5], b[maxn+5];
int z[maxn+5], ext[maxn+5], n, m;
inline void getz(){
int p=0; /** the pos where we have the maximum @p r */
int r=0; /** the maximum @p i+z[i]-1 */
z[1]=m; /** special treatment */
/**
* @warning if @p p==1 , then @p i-p+1==i and we'll invoke @p z[i] , but which hasn't updated yet, so we make @p p==2 initially
*/
rep(i, 2, m){ /** pay attention to the range */
if(i>r || i+z[i-p+1]-1>=r){
if(i>r){
if(b[i]==b[1]) p=r=i;
else{ /** if it failed in the first position */
/** @p r must be set as i-1 to ensure the running time */
p=i, r=i-1, z[i]=0;
continue;
}
}
/** compare @p b[r+1] and @p b[r+1-i+1] */
while(r<m && b[r+1]==b[r+1-i+1]) ++r;
z[i]=r-i+1, p=i; /** pay attention to update @p p */
}
else z[i]=z[i-p+1];
}
}
inline void getext(){
int p=0, r=0;
rep(i, 1, n){
// printf("When i == %d, p == %d, r == %d\n", i, p, r);
// printf("%d + z[%d] - 1 == %d\n", i, i-p+1, i+z[i-p+1]-1);
if(i>r || i+z[i-p+1]-1>=r){
if(i>r){
if(a[i]==b[1]) p=r=i;
else{ /** @brief failed in the first comparison */
/** @p r must be set as i-1 */
p=i, r=i-1, ext[i]=0;
continue;
}
}
/** @brief compare @p a[r+1] and @p b[r+1-i+1] */
while(r<n && r-i+1<m && a[r+1]==b[r+1-i+1]) ++r;
ext[i]=r-i+1, p=i;
}
else ext[i]=z[i-p+1];
}
}
signed main(){
scanf("%s", a+1), scanf("%s", b+1);
n=strlen(a+1), m=strlen(b+1);
getz(); getext();
ll ans=0;
rep(i, 1, m) ans^=1ll*i*(z[i]+1);
writc(ans);
ans=0;
rep(i, 1, n) ans^=1ll*i*(ext[i]+1);
writc(ans);
return 0;
}