\(n\) 字串的長度。
\(m\) 當前字尾(離散化後)的值域。對於char可以跳過離散化,初值取128即可,對於int要離散化,初值取n即可,初值要保證覆蓋整個值域。
\(sa[i]\) 排名為 \(i\) 的字尾的起始位置。
\(rk[i]\) 起始位置為 \(i\) 的字尾的排名。
驗證:
const int maxn = 1000000 + 10;
int n, m, ct[maxn], tp[maxn];
int sa[maxn], rk[maxn], ht[maxn];
void radixsort()
bool compare(int i, int j, int l)
}return 0;
}void suffixsort(char *s)
radixsort();
for(int l = 1;; l <<= 1)
radixsort();
swap(tp, rk);
m = 1;
rk[sa[1]] = 1;
for(int i = 2; i <= n; ++i)
if(m == n)
break;
}}
把字串s迴圈移動,找字典序最小的那個表示。
把字串 \(s\) 複製變成字串 \(s+s\) ,然後變成字尾排序的問題。前[1,n]的字尾陣列中的最小的那個就是答案。當然可能會有多個迴圈表示的串都是代表同乙個東西的,這樣要注意題目的特殊限制,也可以利用這個最小迴圈的串構造出來之後在 \(s+s\) 中查詢第乙個匹配位置。
驗證:這一題只需要把最小表示串找出來,所以就找任意乙個即可。注意n的取值要和字尾陣列中字串相匹配。
int n = strlen(str + 1);
for(int i = 1; i <= n; ++i)
str[i + i] = str[i];
str[n + n + 1] = '\0';
suffixsort(str);
int pos = min_element(rk + 1, rk + 1 + n) - rk;
for(int i = 1; i <= n; ++i)
putchar(str[pos + i - 1]);
putchar('\n');
假如字符集很大,那麼字尾自動機就會失效,這個時候字尾陣列可以通過離散化解決,然後字串取離散化後的結果,m初始值就取n(而不是128)。
找出s的所有迴圈表示,並把他們按字典序排列。
驗證:對於字尾陣列來說,這個問題和上面的一模一樣,直接取前n個的rk重新排序就行。
int n = strlen(str + 1);
for(int i = 1; i <= n; ++i)
str[i + n] = str[i];
suffixsort(str);
for(int i = 1; i <= n; ++i)
p[i] = ;
sort(p + 1, p + 1 + n);
for(int i = 1; i <= n; ++i)
putchar(str[p[i].second + n - 1]);
putchar('\n');
驗證:
對s構造字尾陣列,然後在字尾陣列sa上面二分,二分列舉到乙個位置m,排名為m的字尾的起始位置是sa[m],然後對sa[m]的字尾和模式串t暴力比較,得出的結果可能是m偏大、m偏小,或者剛剛好,找到第乙個剛剛好的位置,然後同理找到最後乙個剛剛好的位置,中間的就是所有的出現次數。因為尋找的是以t開頭的所有字尾的起始位置,所以他們必定是在字尾陣列中連續的乙個區間。
int sl, tl;
char s[maxn];
char t[maxn];
int check(int pos)
int firstequal()
return check(l) == 0 ? l : sl + 1;
}int lastequal()
return check(l) == 0 ? l : 0;
}vi getalloccurences()
下面的約定中,lcp的引數是字串,並且用i表示suf(i),sa[i]表示第i名的字尾,即suf(sa[i])
lcp(i,j)=lcp(j,i)
lcp(i,i)=len(suf(sa[i]))=n-sa[i]+1
$lcp(sa[i],sa[j])=\min\limits_(lcp(sa[k],sa[k-1])) $
即兩個相隔甚遠的字尾的lcp可以用相鄰字尾的lcp的rmq求出來。
設 ht[i]=lcp(sa[i],sa[i-1]) ht[1]=0 即第i名的字尾和它前1名的lcp的長度。
那麼 \(lcp(sa[i],sa[j])=\min\limits_ht[k]\)
那麼ht[rk[i]]>=ht[rk[i-1]]-1
求ht陣列 **
然後,求兩個字尾的lcp就變成rmq問題,可以用st表加速。
子串a[a,b] 和b[c,d]
若lcp(sa[a],sa[c])>=min(|a|,|b|) ,則a貪心取字串的首尾組成字典序最小的新串:比較子串s和子串t的反串的字典序,對s+'#'+s構造字尾陣列。
驗證:所有的子串一共有\(\fracn(n+1)\)個,其中對於每個字尾,重複的恰好是ht的數量。
\(\fracn(n+1)-\sum\limits_^ ht[i]\)
出現至少k次,意味著至少連續k個字尾的lcp是這個串。
故是連續k-1個ht的最小值,列舉所有的最小值求最大值。
題意:給定n=1e5長的字串s,t=100次詢問,每次詢問m=1e3長的字串t,問是否可以從s中選擇兩個不相交的非空子串s1,s2使得s1+s2=t。有乙個雜湊的做法,不過tle22了,列舉s1的長度那麼可以直接算出s2的長度,總共有1e3種長度,在s上跑1e3次尺取雜湊,然後用資料結構找出第乙個雜湊值等於s1的位置和最後乙個雜湊值等於s2的位置比較,複雜度最差為o(nmt)。改用字尾陣列的優勢在於不再需要跑原字串的長度,構造的複雜度為nlogn,然後類似「找字串t的所有出現位置」的做法,找出那個連續區間然後套st表查出來,複雜度是o(nlogn+tmlogn)。st表中傳入的陣列a為字尾陣列sa。
字尾陣列 用字尾處理字串
字尾陣列處理的是文字串。我們將文字串的每一條字尾拿出來,按照字典序排序,然後就可以處理字尾陣列了。字尾陣列sa i 表示的就是排名第i位的字尾的第乙個字元所在下標。這可能有點繞口,所以我們用樣例解釋一下,如對於文字串ababa,則字尾為ababa,baba,aba,ba,a,我們排序後就是 a,ab...
NOIP模擬 字串(字尾陣列)
給定兩個字串 s1 和 s2 兩個字串都由 26 個小寫字母中的部分字母構成。現在需要統計 s 2 在 s 1 中出現了的次數。對於 s1 中的每個位置 i 設 st rlen s2 m,若 j 1m s1 i j 1 s 2 j k 最外層中括號為布林表示式 則認為 s2 在 s1 的 i 處出現...
字串演算法之後綴陣列
字串演算法一直是我最不願碰的東西,包括dp。ababs 中所有的字尾串為 ababs babs abs bs s 我們按照字典序排列即為 ababs abs babs bs s 而字尾陣列就是用來求字尾的字典序的 sa i 為排名為i的字尾第乙個字元在主串裡的位置.譬如上面的例子 sa 1 1,sa...