字尾陣列總結

2021-08-07 09:10:05 字數 3462 閱讀 9170

2子串個數問題

3迴圈子串問題

兩個字串串問題

2子串個數問題

多個字串問題總結

首先,使用倍增演算法求出對應sa,height值,時間複雜度o(

nlog

n)。

(具體定義參考國家集訓隊2009羅穗騫**,**中還給出一種線性dc3做法)

#include

using

namespace

std;

const

int maxn=2e5+50;

int n,m,rk[maxn],sa[maxn],sa2[maxn],c[maxn],h[maxn];

char ch[maxn];

inline

void rsort()

inline

bool cmp(int x,int y,int w)

inline

void getsa()

for(int i=1,k=0,j;i<=n;h[rk[i++]]=k)

for(k?k--:k,j=sa[rk[i]-1];ch[i+k]==ch[j+k];k++);

}

(題目引自國家集訓隊2009羅穗騫**)

1.1重複子串

(定義重複子串:字串r在字串l中至少出現兩次,則稱r是l的重複子串。)

1.1.1可交叉最長重複子串

給乙個字串,求其最長重複子串,該字串出現的位置可交叉。

乙個重複出現的子串必定出現在該字串的兩個位置,且該字串為這兩個位置起始的字尾的最長相同字首,排名中處於鄰位。據此,直接查詢height最大值即可。

1.1.2不可交叉最長重複子串(poj1743)

給乙個字串,求其最長重複子串,該字串出現的位置不可交叉。

首先假設如果存在這個重複子串,根據height定義,這個子串的長度一定是height的某一段的最小值,且這一段存在沒有交叉的位置。那麼二分這個子串長度,判定可行性即可。

1.1.3可交叉的k次最長重複子串

給定乙個字串,求至少出現k次的最長重複子串,這k個子串可以重疊。

同樣,這個重複子串只會出現在排序後的一段連續height中,二分即可。

1.2子串個數問題

1.2.1不相同子串個數(spoj694,spoj705)

給乙個字串,求不同子串的個數

每個子串一定是某個字尾的字首,那麼原問題等價於求所有字尾之間的不相同的字首的個數。

對於每乙個字尾,排序後對答案的貢獻為(n-sa[i]+1)-height[i]。即該字尾的長度減去與前面的字尾重複的字首個數。

1.3迴圈子串問題

1.3.1求最小迴圈節

給定乙個字串l,已知這個字串是由某個字串s重複r次而得到的, 求r的最大值。

同kmp思想,列舉迴圈節長度k(n%k==0),若suffix(1)和suffix(k+1)的最長公共字首為n-k(即suffix(k+1))的長度,必然構成迴圈。

1.3.2重複次數最多的連續重複子串(spoj687,poj3693)
給定乙個字串l,已知這個字串的某個子串可以由某個字串s重複r次而得到, 求r值最大子串(若有多個則輸出字典序最小值)。

這道題有點難,字尾陣列的好題。以下題解來自:

比較容易理解的部分就是列舉長度為l,然後看長度為l的字串最多連續出現幾次。

既然長度為l的串重複出現,那麼str[0],str[l],str[2*l]……中肯定有兩個連續的出現在字串中。

那麼就列舉連續的兩個,然後從這兩個字元前後匹配,看最多能匹配多遠。

即以str[i*l],str[i*l+l]前後匹配,這裡是通過查詢suffix(i*l),suffix(i*l+l)的最長公共字首

通過rank值能找到i*l,與i*l+l的排名,我們要查詢的是這段區間的height的最小值,通過rmq預處理

達到查詢為0(1)的複雜度,

設lcp長度為m, 則答案顯然為m / l + 1, 但這不一定是最好的, 因為答案的首尾不一定再我們列舉的位置上. 我的解決方法是, 我們考慮m % l的值的意義, 我們可以認為是後面多了m % l個字元, 但是我們更可以想成前面少了(l - m % l)個字元! 所以我們求字尾j * l - (l - m % l)與字尾(j + 1) * l - (l - m % l)的最長公共字首。

即把之前的區間字首l-m%l即可。

然後把可能取到最大值的長度l儲存,由於 題目要求字典序最小,通過sa陣列進行列舉,取到的第一組,肯定是字典序最小的。

2.1公共子串問題

2.1.1最長公共子串(poj2774,ural1517)

給定兩個字串a和b,求最長公共子串。

這個沒什麼好說的,兩個字串中間隨便加乙個其他的字元然後拼在一起搞一搞就好了。

2.2子串個數問題

2.2.1特定長度的公共子串

給定兩個字串a和b,求長度不小於k的公共子串的個數(可以相同)。

神題。首先要分組,前面有提到。對於同一組中的每乙個屬於a,b的字尾,要統計這個字尾之前的屬於b,a的字尾的貢獻,發現這個是o(

n2) 的,進一步分析,發現可以用單調佇列來維護。給出**:

for(int i=1;i<=n;i++)

if(cnttmp[j])st[j][++cnt[j]]=make_pair(h[i],cnttmp[j]);//更新後放到同一位置

sum[j]+=1ll*(h[i]-k+1)*cnttmp[j];//更新貢獻

}int bz=0;

if(sa[i]>len1+1)bz=1;

ans+=sum[bz^1];

st[bz][++cnt[bz]]=make_pair(inf,1);sum[bz]+=inf-k+1;//後來的串會更新inf

}

3.1公共子串問題

3.1.1在k個字串中的出現的最長子串(poj3294)

給定n個字串,求出現在不小於k個字串中的最長子串。

二分即可。

(還有,多個字串分隔符較多,可能會爆char,建議換成陣列後再加分隔符)

3.1.2在每個字串中出現k次的最長子串(spoj220)

給定n個字串,求出現在不小於k個字串中的最長子串。

同樣二分。

3.1.3在每個字串中或反轉後出現的最長子串(poj1226)

給定n個字串,求出現或反轉後出現在每個字串中的最長子串。

反轉後連線,二分。

1.二分

2.height分組

字尾陣列總結

附模板 字尾陣列 void build sa int m,int n int x wa,y wb,t for int i 0 i 0 i sa c x i i for int k 1 k n k 1 int p 0 for int i n k i k y p sa i k for int i 0 i...

字尾陣列題目總結

入門部落格 p3809 模板 字尾排序 p4051 jsoi2007 字元加密 將字串s複製為ss,做字尾陣列。p2870 usaco07dec best cow line g 題意 給出乙個字串,每次可以從字串的首位取出乙個字元,放到佇列的尾部,求可以得到的最小的字典序是多少?思路 pre i 表...

SA 字尾陣列 專題總結

曾經一度以為sa是大神知識點來著 後來才發現其實是澤州哥哥講得太深奧了我等蒟蒻不能參透 理解了之後題還是可以做的 因為大多數都不是在sa上做文章而是與其他知識點結合 迪哥講 sa 和 sam 鈦聚啦 放例題算是sa的板子題了吧 求出每個點能控制的區間,單調棧 二分都行,直接統計就行了 sa乙個套路就...