随便写写!!!!!!!!!!!!
假设读者已经知道 \(SA\) 和 \(height\) 数组。定义 \(lcp(i, j)\) 为字符串以 \(i\) 为起点的后缀和以 \(j\) 为起点的后缀的最长公共前缀。
int lcp(int i, int j) {
int l = rnk[i], r = rnk[j];
if (l > r) swap(l, r);
l++;
int len = lg[r - l + 1];
return min(f[l][len], f[r - (1 << len) + 1][len]);
}
最小表示法
设字符串 \(S\) 长度为 \(n\)。把 \(S\) 复制一份得到 \(S + S\),求出 \(SA\) 数组,第一个 \(\leq n\) 的位置就是开始的编号。\(\leq n\) 是为了保证合法。
子串查询
给出 \(T\),在线在 \(T\) 中找 \(S\)。
子串是后缀的前缀。
那么求后缀数组,就是找以 \(S\) 开头的前缀。而且都以 \(S\) 开头,会排在一起,二分一波就可以找到出现位置 & 出现次数了。
处理连续的若干个相同子串
基本就是枚举这个子串的长度,然后根据相邻间隔的 LCP 和 LCS 计算贡献。LCS 可以把原串倒过来求 LCP。
如求重复次数最多的连续重复子串。比如针对 abbabaabaabab
,枚举子串长度,到 \(3\) 的时候,假设相邻两点为 \(l = 4, r = 7\)。算 LCP:abaabaabab
和 abaabab
,可以算到 abaaba
,即两倍的子串;算 LCS:abba
和 aababba
,可以算到 a
。感性理解就是分别统计了两边。这里的话,答案就是 \(\max \{ \dfrac{lcs + lcp - 1}{len} \} + 1\),减去 \(1\) 是因为像上面的例子,如果符合条件,自身的那个字符应该是相等的,会被算一次;加上 \(1\) 是因为当前枚举的这个子串不会被统计。