高一還不會sa就退役吧
uoj#35. 字尾排序
這是一道模板題。
讀入乙個長度為 n
nn 的由小寫英文本母組成的字串,請把這個字串的所有非空字尾按字典序從小到大排序,然後按順序輸出字尾的第乙個字元在原串中的位置。位置編號為 1
11 到 nnn。
除此之外為了進一步證明你確實有給字尾排序的超能力,請另外輸出 n−1
n - 1
n−1 個整數分別表示排序後相鄰字尾的最長公共字首的長度。
輸入格式
一行乙個長度為 n
nn 的僅包含小寫英文本母的字串。
輸出格式
第一行 n
nn 個整數,第 i
ii 個整數表示排名為 i
ii 的字尾的第乙個字元在原串中的位置。
第二行 n−1
n - 1
n−1 個整數,第 i
ii 個整數表示排名為 i
ii 和排名為 i+1
i + 1
i+1 的字尾的最長公共字首的長度。
樣例一input
ababa
output
5 3 1 4 2
1 3 0 2
explanation
排序後結果為:
aaba
ababa
bababa
限制與約定
1 ≤n
≤105
1 \leq n \leq 10^5
1≤n≤10
5時間限制:1
s1\texttt
1s空間限制:256
mb256\texttt
256mb
乙個卵用並不大的東西
sa能做的sam基本都能做,除了o(1)求字尾的lcp
但有些題就是要這麼搞也沒辦法
一些定義:
st:字串
rank[i]:st[i…n]的字典序排名(顯然各不相同)
sa[i]:排名為i的首字母出現位置(rank的逆陣列),即sa[rank[i]]=i
hi[i](即height):st[sa[i-1]…n]和st[sa[i]…n]的lcp長度,hi[i]=h[sa[i]]
h[i]:st[sa[rank[i]-1]…n]和st[i…n]的lcp長度,h[i]=hi[rank[i]]
h[i]即表示以i為結尾的字尾和i排名的上一位的lcp長度
首先是rank的求法
暴力肯定布星,考慮倍增求rank
每次把相鄰的長度為2k段兩段的rank,用二維桶排來求出新的rank(可重,但最終一定不重)
具體:先排個位,再排十位,因為鄰接表的性質,所以要反著提
直接求height不方便,所以引入了h陣列
h陣列有乙個非常顯然且重要的性質:
證明:
顯然h[i]至少為h[i-1]刪掉i-1,即至少為h[i-1]-1
簡單又自然
有了這個性質後就可以線性求出h陣列,然後可以求出height
(有可能sa[rank[i]-1]>i,所以兩個都不能超出邊界)
可以利用height的性質來搞事
st[sa[i]…n]與st[sa[j]…n]的lcp=min(height[i+1…j])
證明:設st[sa[i]…n]與st[sa[j]…n]的lcp長度為x,則x≥min(height[i+1…j])
若x>min(height[i+1…j]),則與字典序連續相違背,所以x≤min(height[i+1…j])
綜上,x=min(height[i+1…j])
對應到原串中,st[i…n]和st[j…n]的lcp長度為min(height[rank[i]+1…rank[j]])(rank[i]然而這題並沒有用到這個性質
以後再填坑
#include
#include
#include
#include
#include
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (ausing
namespace std;
int n,i,j,k,l,len;
int a[
100001];
int pre[
100001];
int ls[
100001];
int st[
100001];
int h[
100001];
//h[i]=hi[rank[i]]
int hi[
100001];
//the lcp of sa[i-1] and sa[i] hi[i]=h[sa[i]]
int rank[
200001];
int sa[
200001];
int rank[
200001];
int bz[26]
;char ch;
intmain()
fo(i,0,
25)bz[i]
+=bz[i-1]
;fo(i,1
,n) rank[i]
=bz[st[i]];
k=1;
while
(k<=n)
l=n;
fd(i,n,0)
}fo(i,1
,n) l=n;
fd(i,n,0)
} j=0;
fo(i,
1,n)
rank[i]
=rank[i];fo
(i,1
,n)
k+=k;}
fo(i,
1,n)
sa[rank[i]
]=i;
fo(i,
1,n)
if(rank[i]
>1)
fo(i,
2,n)
hi[i]
=h[sa[i]];
fo(i,
1,n)
printf
("%d "
,sa[i]);
printf
("\n");
fo(i,
2,n)
printf
("%d "
,hi[i]);
printf
("\n");
}
字串「最」系列
最近練手,整理了乙個 最 系列的主題,這些題目有點繞,個別的還有別名 詳見博文 混在一塊比較亂,就索性放在一起做了個整理,區別的時候要注意子串行與子串的不同,前者不要求連續,後者要求連續 由於大部分跟dp有關,而且一些題目還可以漸進尋求多種解法,可以用來做不錯的練手。下面是這些問題的博文目錄 1 最...
字串系列 word search
題目 已知 乙個二維字元矩陣,乙個單詞 輸出 該單詞是否可以從二維矩陣中拼接出來?拼接規則 從矩陣的某一行的某個字母開始,持續向臨近的字元擴充套件 向上,向下,向左,向右 直至拼接出該單詞。若可以拼出,則輸出false,若拼不出,則輸出false 例如 a,b,c,d,e e,f,g,h,u a,b...
字串dp系列
647.回文子串 給定乙個字串,你的任務是計算這個字串中有多少個回文子串。具有不同開始位置或結束位置的子串,即使是由相同的字元組成,也會被計為是不同的子串。示例 1 輸入 abc 輸出 3 解釋 三個回文子串 a b c 示例 2 輸入 aaa 輸出 6 說明 6個回文子串 a a a aa aa ...