練習 字尾陣列題目

2021-07-11 11:21:58 字數 3819 閱讀 3202

題意:給乙個字串,求最長的出現至少k次的子串,子串可以重疊。

難度:*

和模型一樣,直接上。

code:

#include #include #include using namespace std;

const int max_n = 20005;

int n, k, a[max_n];

void init()

int ws[1000005], wv[max_n], wa[max_n], wb[max_n];

int sa[max_n], h[max_n], r[max_n];

void da(int *a, int *sa, int n, int m)

}void calc()

bool check(int x)

return 0;

}void doit()

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

}int main()

題意:給定乙個字串,每次從當前字串的頭,或者尾去乙個字元,放入新字串的尾部,求字典序最小的新字串。

難度:**

首先每次比較頭尾字元,然後去較小的加入的這個貪心方法是錯誤的。為什麼?考慮當兩個一樣的時候,就不知道怎麼辦了。如果發現一樣,繼續往後列舉比較,這樣的話最壞的時間複雜度為o(n),非常不優美。其實我們列舉後面的字元比較,其實比的是字尾的大小,而這個順序正好可以用字尾陣列求出來。把字串翻轉,用經典方法把兩個字串拼接起來,因為取尾部的字元時實際上比較的是從後往前的」字尾「。然後求rank陣列。用兩個指標搞一搞就o(n)解決了。

code

#include #include #include using namespace std;

const int max_n = 60005;

int n, a[max_n], l1;

char s[30005];

void init()

int ws[max_n], wv[max_n], wa[max_n], wb[max_n];

int sa[max_n], h[max_n], r[max_n];

void da(int *a, int *sa, int n, int m)

}void calc()

void doit()

printf("\n");

}int main()

題意:給定乙個01串,輸出子串出現次數大於1次的次數,按子串的字典序輸出。

難度:**~***

首先根據字尾陣列的一些經典應用,我們可以知道每個字尾對子串個數的貢獻是n-h[i]-sa[i],而且順序就是字典序,我們列舉每個子串暴力的用h陣列前後求匹配:l為向前最多到哪,r為向後最多到哪,答案是r - l。其實還有更簡單的trie樹做法。

code:

#include #include #include using namespace std;

const int max_n = 3005;

int n, a[max_n];

char s[max_n];

void init()

int ws[max_n], wv[max_n], wa[max_n], wb[max_n];

int sa[max_n], h[max_n], r[max_n];

void da(int *a, int *sa, int n, int m)

}void calc()

void doit() }}

int main()

題意:給定乙個字串,求有多少個子串出現至少兩次,子串不能重疊。

難度:**

如果已經會了字尾陣列的一些模型題和上一道題,這道題就變水了,在上一題的基礎上,向前向後匹配的時候再多加乙個看兩個子串是否有重疊的判定就可以了。

code:

#include #include #include using namespace std;

const int max_n = 10005;

int n, a[max_n];

char s[max_n];

void init()

int ws[max_n], wv[max_n], wa[max_n], wb[max_n];

int sa[max_n], h[max_n], r[max_n];

void da(int *a, int *sa, int n, int m)

} void calc()

void doit()

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

}int main()

return 0;

}

題意:給兩個字串, 求lcs。

難度:*(模板題)

code:

#include #include #include using namespace std;

const int max_n = 200005;

char s[100005];

int n, l1, l2, a[max_n];

void init()

int ws[max_n], wv[max_n], wa[max_n], wb[max_n];

int sa[max_n], h[max_n], r[max_n];

void da(int *a, int *sa, int n, int m)

} void calc()

void doit()

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

}int main()

題意:給定乙個字串,ti表示以第i個字元為開始的字尾,求所有len(ti)+len[tj]-2*lcp(ti,tj)之和,其中1<=i

難度:***~****

首先這個式子可以拆成兩部分計算,一部分是兩個字尾長度之和,一部分是lcp長度之和。第一部分很簡單,手推一推就能發現每個長度的字尾都計算了n-1次,所以第一部分的答案為(n-1)*(n*(n+1)/2),注意計算過程中需要強制型別轉換。而第二部分和之前的乙個模型類似,需要用到單調棧。兩個字尾的lcp在h陣列裡其實就是一段區間的最小值,而反過來h陣列每段區間的最小值就對應著兩個字尾的lcp,我們計算總和,不關心都是誰的,所以單調棧維護乙個遞增的h,用f[i]表示h陣列中從i到n所有區間的lcp之和,每次計算f[i],f[i]=f[st[top]]+h[i]*(st[top]-i),然後計入總和就行。

code:

#include #include #include using namespace std;

const int max_n = 500005;

typedef long long ll;

char s[max_n];

int a[max_n], n;

void init()

int ws[max_n], wv[max_n], wa[max_n], wb[max_n];

int sa[max_n], h[max_n], r[max_n];

void da(int *a, int *sa, int n, int m)

} void calc()

ll ans = 0, f[max_n];

int st[max_n];

void doit()

ans -= 2ll * tmp;

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

}int main()

字尾陣列題目總結

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

Codeforces 字尾陣列 題目彙總

模板1 字串從0開始讀入 const int n 2e5 10,inf 0x3f3f3f3f int sa n int rk n int tmp n int lcp n char s n t n int n,k bool cmp int i,int j void get height 123d.st...

字尾陣列題目選講

複習題 luogu題單 1.noi2015 品酒大會 題意 forall i 0,n 求有多少對字尾滿足 lcp ge i 以及滿足條件的兩個字尾的權值乘積的最大值。我們統計出對於 1.n 中的每個 i 統計一下有多少個 lcp i 再做個字尾和。因為 lcp i,j min st times ed...