日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

P4070 [SDOI2016]生成魔咒

發(fā)布時間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P4070 [SDOI2016]生成魔咒 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

P4070 [SDOI2016]生成魔咒

題意:

有n個字符xi,每次在S的末尾加入一個字符,(一開始S為空),每次加入xi后的不相同字串有多少個

題解:

做這個題首先要會后綴數(shù)組P3809 【模板】后綴排序,還要知道不同的子串如何求P2408 不同子串個數(shù),這兩個題我都有寫過博客
對于一個字符串,我們在其末尾加一個新字符,對這個字符串的height等變換還是很大的,整個格局都會被打亂(因為我們是后綴數(shù)組,在最后加,所有后綴都會改變)。但是如果我們在前面加,在最前面加字符,那只會額外產(chǎn)生一個新字符

原本是ABCD
后綴是:ABCD,BCD,CD,D
在末尾加個E
ABCDE
后綴變成:ABCDE,BCDE,CDE,DE,E(沒有一個和之前是一樣的)
如果是在開頭加E
EABCD
后綴就是:EABCD,ABCD,BCD,CD,D(和原先比只多了一個EABCD,我們只需要處理新增就可以)

但是題目就是讓在后面加,怎么搞?
我們可以將這個字符串翻轉(zhuǎn)呀,這樣每次加不就是在最前面加

原本是ABCD
翻轉(zhuǎn):DCBA
加入E
得到:EDCBA
新增后綴EDCBA,現(xiàn)在我們要求新增不重復(fù)字串,按照結(jié)論,后綴長度減去其height[i]

height[i]如何求?
這時,只有插入的那個新后綴緊鄰的height 值發(fā)生了改變,注意到這個值是可以直接在整段字符串的后綴數(shù)組中查到的,即 lcp(prev(i),post(i)) 。
height[i]的變化是O(1)的,我們用一個set每次插入后綴排名,對于以第i位開始的后綴,其排名是rk[i],他的就找他在set中的前一位(到目前位置,已有后綴的前一項),再找后項,兩邊分別取min(height[l~r]),這可以用st來實現(xiàn)。這就是重復(fù)部分,用長度len減去重復(fù)部分,就是后綴的貢獻(xiàn)
詳細(xì)看代碼

代碼:

// Problem: P4070 [SDOI2016]生成魔咒 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P4070 // Memory Limit: 125 MB // Time Limit: 1000 ms // Data:2021-08-23 12:42:42 // By Jozky#include <bits/stdc++.h> #include <unordered_map> #define debug(a, b) printf("%s = %d\n", a, b); using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> PII; clock_t startTime, endTime; //Fe~Jozky const ll INF_ll= 1e18; const int INF_int= 0x3f3f3f3f; void read(){}; template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar) {x= 0;char c= getchar();bool flag= 0;while (c < '0' || c > '9')flag|= (c == '-'), c= getchar();while (c >= '0' && c <= '9')x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();if (flag)x= -x;read(Ar...); } template <typename T> inline void write(T x) {if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0'); } void rd_test() { #ifdef LOCALstartTime= clock();freopen("in.txt", "r", stdin); #endif } void Time_test() { #ifdef LOCALendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC); #endif } const int MAXN= 100005;char ch[MAXN], all[MAXN]; int sa[MAXN], rk[MAXN], height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m; char str[MAXN]; int b[MAXN]; //rk[i] 第i個后綴的排名; sa[i] 排名為i的后綴位置; height[i] 排名為i的后綴與排名為(i-1)的后綴的LCP //tax[i] 計數(shù)排序輔助數(shù)組; tp[i] rk的輔助數(shù)組(計數(shù)排序中的第二關(guān)鍵字),與sa意義一樣。 //a為原串 void RSort() {//rk第一關(guān)鍵字,tp第二關(guān)鍵字。for (int i= 0; i <= m; i++)tax[i]= 0;for (int i= 1; i <= n; i++)tax[rk[tp[i]]]++;for (int i= 1; i <= m; i++)tax[i]+= tax[i - 1];for (int i= n; i >= 1; i--)sa[tax[rk[tp[i]]]--]= tp[i]; //確保滿足第一關(guān)鍵字的同時,再滿足第二關(guān)鍵字的要求 } //計數(shù)排序,把新的二元組排序。int cmp(int* f, int x, int y, int w) {return f[x] == f[y] && f[x + w] == f[y + w]; } //通過二元組兩個下標(biāo)的比較,確定兩個子串是否相同void Suffix() {//safor (int i= 1; i <= n; i++)rk[i]= a[i], tp[i]= i;m= 127, RSort(); //一開始是以單個字符為單位,所以(m = 127)for (int w= 1, p= 1, i; p < n; w+= w, m= p) { //把子串長度翻倍,更新rk//w 當(dāng)前一個子串的長度; m 當(dāng)前離散后的排名種類數(shù)//當(dāng)前的tp(第二關(guān)鍵字)可直接由上一次的sa的得到for (p= 0, i= n - w + 1; i <= n; i++)tp[++p]= i; //長度越界,第二關(guān)鍵字為0for (i= 1; i <= n; i++)if (sa[i] > w)tp[++p]= sa[i] - w;//更新sa值,并用tp暫時存下上一輪的rk(用于cmp比較)RSort(), swap(rk, tp), rk[sa[1]]= p= 1;//用已經(jīng)完成的sa來更新與它互逆的rk,并離散rkfor (i= 2; i <= n; i++)rk[sa[i]]= cmp(tp, sa[i], sa[i - 1], w) ? p : ++p;}//離散:把相等的字符串的rk設(shè)為相同。//LCPint j, k= 0;for (int i= 1; i <= n; height[rk[i++]]= k)for (k= k ? k - 1 : k, j= sa[rk[i] - 1]; a[i + k] == a[j + k]; ++k);//這個知道原理后就比較好理解程序 } int st[MAXN][30]; void ST() {for (int i= 1; i <= n; i++)st[i][0]= height[i];int w= log2(n);for (int k= 1; k <= w; k++) {for (int i= 1; i <= n; i++) {if (i + (1 << k) > n + 1)break;st[i][k]= min(st[i][k - 1], st[i + (1 << (k - 1))][k - 1]);}} } int get_min(int l, int r) {int k= log2(r - l + 1);return min(st[l][k], st[r - (1 << k) + 1][k]); } void Init() {read(n);for (int i= 1; i <= n; i++) {read(a[i]);b[i]= a[i];}sort(b + 1, b + 1 + n);int len= unique(b + 1, b + 1 + n) - (b + 1);reverse(a + 1, a + 1 + n);for (int i= 1; i <= n; i++) {a[i]= lower_bound(b + 1, b + 1 + len, a[i]) - b;} } set<int> s;int main() {Init();Suffix();ST();// for (int i= 1; i <= n; i++)// printf("height[i]=%d\n", height[i]);ll ans= 0;for (int i= n; i; i--) { //倒序考慮s.insert(rk[i]);set<int>::iterator it;it= s.find(rk[i]);int lcp= 0;if (it != s.begin()) { //如果沒到開頭,說明前面有height,和前面取minint p= *(--it);lcp= get_min(p + 1, rk[i]);++it; //還原}/*注意:s.begin();返回指向容器最開始位置數(shù)據(jù)的指針而s.end();返回指向容器最后一個數(shù)據(jù)單元+1的指針*/++it;if (it != s.end()) {int p= *it;lcp= max(lcp, get_min(rk[i] + 1, p));}int len= n - i + 1;ans+= len - lcp;printf("%lld\n", ans);} }

總結(jié)

以上是生活随笔為你收集整理的P4070 [SDOI2016]生成魔咒的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。