kuangbin帶你飛 專題十八 字尾陣列

2021-08-08 21:42:04 字數 3035 閱讀 9059

寫了好久才把字尾陣列的專題寫完= =。。。

字尾陣列就是對字串的所有字尾來搞事,通過對這些字尾排序,來得到這些字尾之前存在的關係。

字尾陣列中的sa陣列(排名陣列)和height(相鄰排名lcp)有很多很有用的特性,使得在處理一些字串問題的時候很給力,比較流行的求字尾陣列的演算法有o(nlogn)的倍增求法和o(n)的dc3(並不會)。

height陣列滿足特性:sa[i]與saj的最長公共字首是height[i+1]到height[j]這段區間的最小值。所以問題就可以轉化成求區間最小值,同樣也可以用倍增法解決這個問題,達到o(nlogn)初始化和o(1)查詢。

跟sa相關的題目很多都涉及了多字串,常用策略就是將字串全都拼接在一起,中間使用不同的不會出現的字元分隔開他們;還有height陣列分組也是比較常見的做法,將連續一段滿足height全都大於等於k的分成一組,這組裡的所有字尾相互就都滿足最長公共字首大於等於k,如果這裡面的字尾來自n個不同的字串,那就說明了這n個字串內存在了長度為k的公共子串。

同時,sa陣列是字尾的字典序排序,所以有些題目需要字典序最小答案也可以通過sa陣列輕易的獲得。

二分答案判斷是否滿足也是比較常見的解題方法。

kuangbin大神的字尾陣列專題裡的題目幾乎都是來自那篇著名的字尾陣列的**,所以都可以在裡面找到解題思路。

求陣列裡不重疊的變化相同的最長公共子串。

將陣列處理一下,a[i]變成a[i]-a[i-1],陣列就符合題目要求了,二分答案k,然後就可以用上述的height陣列分塊的方法將每乙個height陣列都大於等於k的區間分為一塊,要求不重疊,就是要使得分塊中sa最大值和最小值對應的字尾下標差大於等於k。

#include

#include

using

namespace

std;

const

int maxn=20005;

const

int maxm=1005;

int n;

int r[maxn];

int sa[maxn],t1[maxn],t2[maxn],c[maxn];

int rank[maxn],height[maxn];

bool cmp(int *r,int a,int b,int l)

void get_sa(int str,int sa,int rank,int height,int n,int m)

int k = 0;

n--;

for(i = 0;i <= n;i++)rank[sa[i]] = i;

for(i = 0;i < n;i++)

}bool func(int k)

if(maxx-minn>=k)return

1; maxx=minn=sa[i];

}return0;}

int main()

else rig=mid-1;

}ans++;

if(ans<5)printf("0\n");

else

printf("%d\n",ans);

}}

求字串中出現了k次的可重疊子串,大體做法和上面那題差不多,二分答案後判斷的方式略有不同,存在乙個height陣列的分塊大於等於k就滿足條件。

#include

#include

using

namespace

std;

typedef

long

long ll;

const

int maxn=20015;

const

int maxm=10005;

const ll mod=1e9+7;

int n,m,k;

struct nodenum[maxn];

bool cmp1(node a,node b)

void build_sa(int n,int m)

bool judge(int len)

else

if(cnt>=k)

}return0;}

int main()

r[num[i].id]=cur;

}r[n]=0;

build_sa(n,n+1);

int lef=0,rig=n,ans=0;

while(lef<=rig)

else rig=mid-1;

}printf("%d\n",ans);

return

0;}

尋找字串中不相同子串的數量,也是可以利用height陣列完成的事情,height[i]表示sa[i]和sa[i-1]的公共字首,n-sa[i-1]-height[i]就是sa[i-1]對應的字尾可以產生的與sa[i]不同的子串,正著掃一邊就可以處理出答案來了。

#include

#include

#include

using

namespace

std;

const

int maxn=1005;

const

int maxm=400005;

int n,cas;

char str[maxn];

int r[maxn];

int ans[maxn];

int sa[maxn],t1[maxn],t2[maxn],c[maxn];

int rank[maxn],height[maxn];

bool cmp(int *r,int a,int b,int l)

void get_sa(int n,int m)

int k = 0;

n--;

for(i = 0;i <= n;i++)rank[sa[i]] = i;

for(i = 0;i < n;i++)

}struct nodein[maxn];

bool cmp1(node a,node b)

printf("%d\n",ans);

}return

0;}

kuangbin帶你飛專題

kuangbin帶你飛 專題一 簡單搜尋 kuangbin帶你飛 專題二 搜尋高階 kuangbin帶你飛 專題三 dancing links kuangbin帶你飛 專題四 最短路練習 kuangbin帶你飛 專題五 並查集 kuangbin帶你飛 專題六 最小生成樹 kuangbin帶你飛 專題...

kuangbin帶你飛 專題一 簡單搜尋 D

n m的0 1格仔,目標是全變成0 每次反轉乙個,身邊的4個都變成相反的 0變1,1變0 問最少反轉多少個,每個格仔如果翻了填1,沒翻填0 乍一看挺恐怖的,首先想到乙個格仔不可能翻轉兩次,因為反轉兩次就變回了原來的樣子,就是相當於浪費,那麼每個格仔就是翻轉或者不翻轉兩種可能,而當第一行確定下來翻轉與...

kuangbin帶你飛專題1(搜尋)A 棋盤問題

先上題 在乙個給定形狀的棋盤 形狀可能是不規則的 上面擺放棋子,棋子沒有區別。要求擺放時任意的兩個棋子不能放在棋盤中的同一行或者同一列,請程式設計求解對於給定形狀和大小的棋盤,擺放k個棋子的所有可行的擺放方案c。input 輸入含有多組測試資料。每組資料的第一行是兩個正整數,n k,用乙個空格隔開,...