字串 字尾陣列

2022-05-11 05:57:31 字數 3154 閱讀 6986

\(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...