\(sa[i]\) 表示第 \(i\) 小的字尾對應原串的位置
\(rk[i]\) 表示第 \(i\) 個字尾的排名
\(x[i]\) 表示第 \(i\) 個字尾的第一關鍵字排名,即當前的 \(rk[i]\)
\(y[i]\) 表示第 \(i\) 小的第二關鍵字對應的第幾個字尾
\(c[i]\) 是乙個計數陣列,用於基數排序用
考慮倍增,每次從 \(2^k\) 轉移到 \(2^\) ,可以發現每個 \(2^\) 串可以表示為兩個 \(2^k\) 的子串,分別對應兩個關鍵字。
用基數排序即可。
複雜度 \(o(nlogn)\)
定義 \(lcp(i,j)\) 表示 \(suf(sa[i])\) 和 \(suf(sa[j])\) 的最長公共字首長度。
顯然有:
考慮乙個強大的結論:
對於任意 \(1\le i< k< j\le n\),均有 \(lcp(i,j)=min(lcp(i,k),lcp(k,j))\)
證明:設 \(p=min(lcp(i,k),lcp(k,j))\) ,則有 \(lcp(i,k)\ge p, lcp(k,j)\ge p\) 。
設 \(suf(sa[i]) = u, suf(sa[k]) = v, suf(sa[j]) = w\) ,
則由 \(lcp\) 定義知 (\(u\) 和 \(v\))、(\(v\) 和 \(w\)) 前 \(p\) 個字元均相等。
因此 \(u\) 和 \(w\) 的前 \(p\) 個字元相等,所以 \(lcp(i,j)\ge p\)
假設 \(lcp(i,j)=q > p\) ,那麼 \(q\ge p + 1\) ,即 \(u[p+1]=w[p+1]\) 。
因為 \(p=min(lcp(i,k),lcp(k,j))\) ,所以 \(u[p+1]≠v[p+1]\) 或 \(v[p+1]≠w[p+1]\)
因為 \(suf(sa[i]),所以 \(u[p+1]=v[p+1]=w[p+1]\)
矛盾,故 \(lcp(i,j)\le p\)
因此 \(lcp(i,j)=p=min(lcp(i,k),lcp(k,j))\) 。
終極結論:
\(lcp(i,j)=min(lcp(k-1,k) | i < k\le j)\)
證明:由 \(lcp\ lemma\) 知 \(lcp(i,j)=min(lcp(i,j-1),lcp(j-1,j))=min(min(lcp(i,j-2),lcp(j-2,j-1)),lcp(j-1,j))=...=min(lcp(k-1,k) | i < k\le j)\)
前面都是鋪墊。
我們設 \(height[i]=lcp(i-1,i)\) (\(1) ,顯然 \(height[1] = 0\)
由 \(lcp\ theorem\) 知 \(lcp(i,j)=min(height[k] | i
那麼,這個 \(height\) 該咋求呢?
繼續定義 \(h[i] = height[rk[i]]\) ,則有 \(height[i] = h[sa[i]]\)
下面來證明乙個最關鍵的定理:\(h[i]\ge h[i-1]-1\) ,即 \(height[rk[i]]\ge height[rk[i-1]] - 1\) 。
也即 \(lcp(rk[i]-1,rk[i])\ge lcp(rk[i-1]-1,rk[i-1])-1\) 。
證明:不妨設第 \(i-1\) 個字串按排名來前面的是第 \(k\) 個字串。
由 \(height\) 定義知第 \(k\) 個字串和第 \(i-1\) 個字串的 \(lcp\) 為 \(height[rk[i-1]]\)
下面我們來討論兩字串間的關係:
那麼第 \(k+1\) 個字串的排名既有可能在 \(i\) 前面,也有可能在 \(i\) 後面。
但沒關係,此時 \(height[rk[i-1]]=0\) ,一定滿足 \(height[rk[i]] \ge height[rk[i-1]] - 1\)
由於第 \(k+1\) 個字串就是第 \(k\) 個字串去掉首字元得到的,第 \(i\) 個字串就是第 \(i-1\) 個字串去掉首字元得到的,所以第 \(k+1\) 個字串和第 \(i\) 個字串的 \(lcp\) 就是 \(height[rk[i-1]]-1\)
注意第 \(k+1\) 個字元不一定是第 \(sa[rk[i]-1]\) 個字串,但是因為第 \(sa[rk[i]-1]\) 個字串和第 \(i\) 個字串的 \(lcp\) 最大,即 \(height[rk[i]]=lcp(rk[i]-1,rk[i])\ge lcp(rk[k+1],rk[i])=height[rk[i-1]]-1\)
綜上所述,可證得 \(h[i]\ge h[i-1]-1\)
貼份**,跑路
// author: wlzhouzhuan
#pragma gcc optimize(2)
#pragma gcc optimize(3)
#include using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair #define mp(a, b) make_pair(a, b)
#define debug(x) cerr << #x << " = " << x << '\n';
#define pll pair #define fir first
#define sec second
inline int read()
while (isdigit(op))
return neg * x;
}inline void print(int x)
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}const int n = 1000005;
char s[n];
int n, m;
int sa[n], rk[n], x[n], y[n];
int cnt[n];
void sa()
if (p == n) break;
m = p;
} memcpy(rk, x, sizeof(x));
}int height[n], h[n][20], lg[n];
int getheight()
for (rint j = 1; j < 20; j++)
}}int lcp(int l, int r)
int main()
puts("");
lg[1] = 0;
for (rint i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
int l, r;
while (~scanf("%d%d", &l, &r))
return 0;
}
字尾陣列複習小記
字尾陣列,顧名思義就是處理字尾的陣列。例如daabbc的字尾 daabbc,aabbc,abbc,bbc,bc,c六個字尾。定義sa i 為排名第i個的字尾的第乙個字元在原字串中的序號。如上面的sa 1 2,因為字尾從序號2開始的aabbc排第乙個。定義rank i 為序號從i開始的字尾排第幾個。如...
字尾陣列複習小記
定義 suf i 表示以i為開頭的字尾 rank i 表示suf i 的排名,sa i 表示排名為i的字尾 height i 表示sa i 和sa i 1 的lcp h i 表示suf i 和suf sa rank i 1 的lcp sa rank i i,所以只要能求出rank,就可以求sa 倍增...
字尾樹 字尾陣列
在字串處理當中,字尾樹和字尾陣列都是非常有力的工具,其中字尾樹大家了解得比較多,關於字尾陣列則很少見於國內的資料。其實字尾陣列是字尾樹的乙個非 常精巧的替代品,它比字尾樹容易程式設計實現,能夠實現字尾樹的很多功能而時間複雜度也不太遜色,並且,它比字尾樹所占用的空間小很多。可以說,在資訊學競賽 中字尾...