洛谷模板 & \(\texttt{My solution}\)函數
擴展 KMP:求出字符串 \(S\) 的全部後綴與 \(T\) 的最長公共前綴長度,時間複雜度 \(\mathcal{O}(|S|+|T|)\)。spa
該解法思想與 KMP 相似,因此稱做擴展 KMP。code
定義 \(next_i\) 爲 \(T\) 由 \(i\) 開始的後綴與 \(T\) 的最長公共前綴長度。(\(z\) 函數)blog
這是本題的第一問,也是第二問的輔助數組。字符串
顯然 \(next_0=|T|\),定義 \(n=|T|\)。get
考慮求出了 \(next_{0\cdots x-1}\),如今要求 \(next_x\)。string
注:如下的圖顏色相同的部分的字串都相等。io
定義 \(k=\max\{j+next_j-1\}(0\le j\lt x)\) 的 \(j\),\(p=k+next_k-1\)。模板
根據 \(next\) 定義,可得 \(T\) 的前綴 \(T_{0\cdots next_k-1}=T_{k\cdots n}\) 的後綴 \(T_{k\cdots p}\)。
咱們在 \(T_{k\cdots p}\) 中把 \(T_{x\cdots p}\) 標註出來,由於兩段灰色部分相同,能夠在第一段裏找到相同的位置 \(T_{x-k\cdots next_k-1}\)。
咱們找到 \(next_{x-k}\),記做 \(y\)。因爲 \(next\) 的定義,\(T_{0\cdots y-1}=T_{x-k\cdots x-k+y-1}\),在右邊的灰色部分,也有一段和它徹底同樣的 \(T_{x\cdots x+y-1}\)。
狀況一:藍色部分小於等於紅色部分,此時肯定 \(next_x=y\)
狀況二:藍色部分大於紅色部分,此時發現 \(x+y-1\) 已經超過了咱們目前去到的最遠的地方,所以從 \(p,p-x+1\)(就是第一段藍色中位置大於紅色長度的那一部分) 開始一位一位比,算出真正的 \(next_x\)。(記得要更新 \(k\) 和 \(p\))
\(p\) 是咱們當前已匹配到最遠的位置,狀況一 \(k\) 不會變,狀況二 \(k\) 會變大,\(k\) 保持不降,時間複雜度 \(\mathcal O(|T|)\)
\(extend_i\) 數組表示 \(S\) 從 \(i\) 開始的後綴與 \(T\) 的最長公共前綴長度。(第二問)
和 \(next\) 求法很是像。仍是考慮求出了 \(extend_{0\cdots x-1}\),如今求 \(extend_x\)
仍是定義 \(k=\max\{j+extend_j-1\}(0\le j\lt x)\) 的 \(j\),\(p=k+extend_k-1\)。
在圖中畫出 \(x\),\(x-k\),標紅色。
定義 \(y=next_{x-k}\),標出相同的三段藍色。
這時仍是能夠分爲兩種狀況。藍色部分小於等於紅色部分,存在 \(extend_x=y\),不然須要從 \(p,p-x+1\) 開始一一比對肯定 \(extend_x\)。
因爲此時的 \(k\) 不降,時間複雜度 \(\mathcal O(|S|)\)
個人代碼中 \(next\to nxt,extend\to ext\)
#include<stdio.h> #include<string.h> const int maxn = 2e7 + 1; const int& max2(const int& a,const int& b){return a > b ? a : b;} char s[maxn],t[maxn]; int n,m,nxt[maxn],ext[maxn]; void calc_nxt(){ nxt[0] = n; int j = 0; while(j + 1 < n && t[j] == t[j + 1]) ++j; nxt[1] = j; // nxt[0],nxt[1] 暴力算 int k = 1; // 我是把k放在循環外,p用k獲得,更新的也是k for(int i = 2;i < n;++i){ int p = k + nxt[k] - 1; if(i + nxt[i - k] <= p) nxt[i] = nxt[i - k]; // 狀況一 else { j = max2(p - i + 1,0); // 狀況二 while(i + j < n && t[i + j] == t[j]) ++j; // 狀況二,暴力一位一位比對 nxt[i] = j,k = i; // 記得更新k } } } void calc_ext(){// 和剛纔幾乎同樣 int j = 0; while(j < n && j < m && s[j] == t[j]) ++j; ext[0] = j; // ext[0] 暴力算 int k = 0; // 放在循環外 for(int i = 1;i < m;++i){ int p = k + ext[k] - 1; if(i + nxt[i - k] <= p) ext[i] = nxt[i - k];// 省略講解裏的y else { j = max2(p - i + 1,0); // 取max while(i + j < m && j < n && s[i + j] == t[j]) ++j; // 一位一位比對 ext[i] = j,k = i; } } } int main(){ scanf("%s%s",s,t); n = strlen(t),m = strlen(s); calc_nxt();calc_ext(); long long res1 = 0,res2 = 0; for(int i = 0;i < n;++i) res1 ^= 1LL * (i + 1) * (nxt[i] + 1); for(int i = 0;i < m;++i) res2 ^= 1LL * (i + 1) * (ext[i] + 1); printf("%lld\n%lld\n",res1,res2); return 0; } // 拜拜qwq~ (你覺得我會讓你直接複製代碼?