exkmp 用於求解這樣的問題:
求文本串 的每一個後綴與模式串 的匹配長度(即最長公共前綴長度)。特別的,取 ,得到的這個長度被稱爲 函數。“函數”只是一個叫法,它本質上是個數組…爲了好聽,後面叫他“ 數組” (互聯網上的確有人這麼叫)
符號(字符串)
表示 的長度
表示 從 到 的子串。如果 空着,默認爲 ;同理 默認爲 。
也就是 表示 到 結束的前綴, 表示 從 開始的後綴。
表示 的 最長公共前綴 (Longest Common Prefix
)
算法講解
設
定義從 開始的匹配區間爲 (設 )
我們枚舉處理。假設現在已經求好了 的 數組,要求 。記錄一個 最靠後 的匹配區間 (,以 靠後爲第一關鍵字, 靠後爲第二關鍵字),考慮直接從 中繼承點答案來,那很顯然一個前提就是 (你 在 外面繼承啥)
顯然, (因爲 是 前綴)
由定義, 是最長匹配長度,可知 。
然後現在假如 ,那麼顯然
那麼
簡單想一下,
我們要求 子串與整個串的 ,可以先求以 開頭的整個後綴的與整個串的 ,然後和區間長度取 。這顯然正確。
然後有:
右邊的 兩個抵消了,就變成
然後前面是 。這不就是 的 數組的第 個位置嗎!(還記得 數組的定義嗎?)
覺得看字母理解不了的看圖(自己畫的)(純鼠標):
紅色的部分就是我們推出來的匹配部分。然後現在我們把 移到 開頭的位置來匹配,就相當於把 這一段(紅色)移到 的開頭處匹配。這一段匹配的長度就是 。
假設我們現在能求這個 數組,那麼我們已經知道 的最小值了 ,就是 。從這個位置開始暴力即可。這樣就不用每次從 開始匹配了。
求完 之後,記得用 更新 。
時間是線性的,我不會證,可以參考網上的證明。
如何求 Z 數組
我們發現 數組就是自己和自己匹配的過程。然後我們把上面過程中 換成 即可。
所以我們還是記錄一個最靠後的匹配區間 ,然後 就相當於 了。
易得:
求完 之後,記得用 來更新 。
一樣,也是從這裏開始暴力即可。時間複雜度依然是線性的,可以參考網上的證明。
模板
#include <bits/stdc++.h>
using namespace std;
#define N 20000007
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define Tra(i,u) for(int i=G.Start(u),v=G.To(i);~i;i=G.Next(i),v=G.To(i))
#define p_b push_back
#define sz(a) ((int)a.size())
#define all(a) a.begin(),a.end()
#define iter(a,p) (a.begin()+p)
#define Flandre_Scarlet int
#define IsMyWife main
char _c;
int I()
{
int x=0; int f=1;
while(_c<'0' or _c>'9') f=(_c=='-')?-1:1,_c=getchar();
while(_c>='0' and _c<='9') x=(x<<1)+(x<<3)+(_c^48),_c=getchar();
return (x=(f==1)?x:-x);
}
void Rd(int cnt,...)
{
va_list args; va_start(args,cnt);
F(i,1,cnt) {int* x=va_arg(args,int*);(*x)=I();}
va_end(args);
}
char a[N],b[N];
void Input()
{
scanf("%s%s",a+1,b+1);
}
int z[N];
void Z(char s[]) // 求 Z 函數
{
int n=strlen(s+1);
z[1]=n; F(i,2,n) z[i]=0;
// Z[1]=n 特判,同時也是遞推邊界
int l=0,r=0;
F(i,2,n)
{
if (i<=r) z[i]=min(z[i-l+1],r-i+1); // 推理出下界
while(i+z[i]<=n and s[i+z[i]]==s[z[i]+1]) ++z[i]; // 暴力
if (i+z[i]-1>=r) l=i,r=i+z[i]-1; // 更新最靠後的匹配位置
}
}
int p[N];
void ExKmp(char s[],char t[])
{
int n=strlen(s+1);
Z(t);
int l=0,r=0;
F(i,1,n)
{
if (i<=r) p[i]=min(z[i-l+1],r-i+1); // 推理出下界
while(i+p[i]<=n and s[i+p[i]]==t[p[i]+1]) ++p[i]; // 暴力
if (i+p[i]-1>r) l=i,r=i+p[i]-1; // 更新最靠後的匹配位置
}
}
void Soviet()
{
ExKmp(a,b);
int n=strlen(a+1),m=strlen(b+1);
long long ans=0;
F(i,1,m) ans^=1ll*i*(z[i]+1);
printf("%lld\n",ans);
ans=0;
F(i,1,n) ans^=1ll*i*(p[i]+1);
printf("%lld\n",ans);
}
Flandre_Scarlet IsMyWife()
{
Input();
Soviet();
getchar();
return 0;
}