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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CF1037H Security——SAM+线段树合并

發布時間:2025/5/22 编程问答 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CF1037H Security——SAM+线段树合并 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

又是一道\(SAM\)維護\(endpos\)集合的題,我直接把CF700E的板子粘過來了QwQ

思路

如果我們有\([l,r]\)對應的\(SAM\),只需要在上面貪心就可以了。因為要求的是字典序比\(T\)大且最小的子串,我們從前到后讓盡可能多的位相等,如果再也無法相等了就從后往前找一位變大。
但是每次詢問會給我們一個可行區間\([l,r]\),而我們又顯然不能直接把對應區間的\(SAM\)建出來,否則復雜度會\(GG\)
仔細想一下,其實我們并不需要知道\([l,r]\)區間對應的\(SAM\),只要知道能否向某一個結點轉移就行了。這個我們可以用\(endpos\)集合來判斷。具體一下,就是當前已經考慮了\(i\)個字符且在結點\(u\),現在需要判斷能否轉移到\(v\),只需要判斷\(u\)是否有到\(v\)的轉移邊和\(v\)結點的\(endpos\)是否有元素在\([l+i-1,r]\)中就行了
\(endpos\)拿線段樹合并維護一下就行了(也可以用樹上主席樹搞一下)
其實本菜雞來學這個套路完全是為寫你的名字做鋪墊的

#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <string> #include <vector> #include <cmath> #include <ctime> #include <queue> #include <map> #include <set>using namespace std;#define IINF 0x3f3f3f3f3f3f3f3fLL #define ull unsigned long long #define pii pair<int, int> #define uint unsigned int #define mii map<int, int> #define lbd lower_bound #define ubd upper_bound #define INF 0x3f3f3f3f #define vi vector<int> #define ll long long #define mp make_pair #define pb push_back#define N 200000 #define Q 200000int n, q, m; char s[N+5], t[N+5], ans[N+5];struct SAM {int nxt[26][2*N+5], maxlen[2*N+5], link[2*N+5], tot, lst;int sumv[100*N+5], ch[2][100*N+5], root[2*N+5], nid;int node[N+5];vi G[2*N+5];void init() {tot = lst = 1;nid = 0;}void pushup(int o) {sumv[o] = sumv[ch[0][o]]+sumv[ch[1][o]];}void add(int &o, int l, int r, int x) {if(!o) o = ++nid;if(l == r) {sumv[o] = 1;return ;}int mid = (l+r)>>1;if(x <= mid) add(ch[0][o], l, mid, x);else add(ch[1][o], mid+1, r, x);pushup(o);}int merge(int o, int u, int l, int r) {if(!o || !u) return o|u;int v = ++nid;if(l == r) {sumv[v] = sumv[o]+sumv[u] ? 1 : 0;return v;}int mid = (l+r)>>1;ch[0][v] = merge(ch[0][o], ch[0][u], l, mid);ch[1][v] = merge(ch[1][o], ch[1][u], mid+1, r);pushup(v);return v;}int query(int o, int l, int r, int L, int R) {if(L > R || !o) return 0;if(L <= l && r <= R) return sumv[o];int ret = 0, mid = (l+r)>>1;if(L <= mid) ret += query(ch[0][o], l, mid, L, R);if(R > mid) ret += query(ch[1][o], mid+1, r, L, R);return ret;}void extend(int c) {int cur = ++tot;maxlen[cur] = maxlen[lst]+1;while(lst && !nxt[c][lst]) nxt[c][lst] = cur, lst = link[lst];if(!lst) link[cur] = 1;else {int p = lst, q = nxt[c][p];if(maxlen[q] == maxlen[p]+1) link[cur] = q;else {int clone = ++tot;maxlen[clone] = maxlen[p]+1;link[clone] = link[q], link[q] = link[cur] = clone;for(int i = 0; i < 26; ++i) nxt[i][clone] = nxt[i][q];while(p && nxt[c][p] == q) nxt[c][p] = clone, p = link[p];}}lst = cur;}void dfs(int u) {for(int i = 0, v; i < G[u].size(); ++i) {v = G[u][i];dfs(v);root[u] = merge(root[u], root[v], 1, n);}}void build() {init();for(int i = 1; i <= n; ++i) {add(root[tot+1], 1, n, i);extend(s[i]-'a');}for(int i = 2; i <= tot; ++i) G[link[i]].pb(i);dfs(1);}void search(int L, int R) {m = strlen(t+1);int u = 1, v, x;for(int i = 1; 1; ++i) {ans[i] = -1;for(int j = max(t[i]-'a'+1, 0); j < 26; ++j) {v = nxt[j][u];if(v && query(root[v], 1, n, L+i-1, R)) {ans[i] = j;break;}}v = nxt[max(t[i]-'a', 0)][u];x = i;if(i == m+1 || !v || !query(root[v], 1, n, L+i-1, R)) break;u = v;}while(x && ans[x] == -1) --x;if(!x) printf("-1\n");else {for(int j = 1; j < x; ++j) printf("%c", t[j]);printf("%c\n", ans[x]+'a');}} }sam;int main() {scanf("%s%d", s+1, &q);n = strlen(s+1);sam.build();for(int i = 1, L, R; i <= q; ++i) {scanf("%d%d%s", &L, &R, t+1);sam.search(L, R);}return 0; }

轉載于:https://www.cnblogs.com/dummyummy/p/10937663.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的CF1037H Security——SAM+线段树合并的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。