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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

[题集]串

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

字典序問題

求最優(yōu)化:按位貪心

第k小

按位枚舉,看滿足的是否存在k個

定義集合字典序為排序后的字典序

第k大子集:按位貪心(二分?看情況)

比某個集合大k:枚舉LCP「LibreOJ NOI Round #1」驗題

?

字典序比較:枚舉LCP

例題1

給出 ? 個字符串,字符集為 10
每次輸入一個 10 的排列來規(guī)定 10 種字符的字典序
詢問第 ? 個串在這種字典序下排名第幾

LCP

建trie樹,兩者字典序不同一定有分叉,而分叉走下去的字符是比較關(guān)鍵。cnt[a][b]表示到根路徑上,因為字符a,b之差而分叉的個數(shù)

離線+dfs

已知小串的子串問題

套路:KMP、AC 自動機
AC 自動機和子串的位置關(guān)系、fail 指針

例題 1

? 給出一棵樹
? 每次給出一個串,詢問一條路徑上這個串作為子串出現(xiàn)了多少次
? ?, ? ≤ 10

經(jīng)典問題

離線,差分,阿貍的打字機

跨域LCA部分暴力

?

例題2

你需要維護一棵 Trie
每次插入一個葉子節(jié)點,然后你要輸出這個葉子節(jié)點到根的這條
路徑的最長的“前綴 = 后綴”的長度

?, ? ≤ 1000000

就是KMP了。

不能暴力跳,利用fail指針加速

但是必須是在這個到根鏈上的出邊

所以建主席樹

每個點對于每個分叉出去的邊都建一個往這方向走的到根路徑上的出邊集合(和fail的出邊合并)

每個點再維護一個初始的到根路徑上的主席樹

新加入一個點x,通過fa找到fail,x初始主席樹就是fail的這個方向出邊的主席樹

然后fa對于這個方向,新建一個主席樹,這個出邊節(jié)點指向x

?

例題3

? 你有兩個串集合 S 和 T,S 是固定的,T 會在操作中修改
? 每次操作:T 集合加一個串、詢問 T 里面有多少個串包含 S[i]

?

bzoj3881: [Coci2015]Divljak

S建AC自動機

加入一個T,途經(jīng)的點直接fail樹上所有的點都會增加一次出現(xiàn),

為了不重復,就是鏈并上的點+1

LCT維護fail樹,直接tag標記表示每個點最近一次顏色是哪一個

access時候判斷是否已經(jīng)是當前顏色即可。

O(nlogn)

?

已知大串的子序列問題

?

? 一般模式:給出一個大串,求其子序列的一種信息
? 包括最優(yōu)化和計數(shù)兩類問題
? 其中計數(shù)問題包括兩種:位置不同算多種/內(nèi)容不同算多種
? 包括一系列公共子序列問題
? 對于位置不同的問題(或者最優(yōu)化問題),只需要記錄 ? ? 這樣
的狀態(tài)即可,表示所有右端點不超過 ? 的子序列的信息
? 對于內(nèi)容不同的問題,我們需要另一些思想

?

記錄每個位置每個字符下一次第一個出現(xiàn)的位置

可建DAG,也即序列自動機

可以直接進行匹配和統(tǒng)計

內(nèi)容不同子序列計數(shù)問題

例題1

求一個字符串有多少內(nèi)容不同的回文子序列

? = 5000, Σ = 10

f[l][r]表示,以l,r結(jié)尾的答案

f[l][r]=∑f[nxt[w]][las[w]]+(l<=r)+(l<r)

也就是往里面選擇一些+自己

記憶化搜索

首尾加上相同特殊字符,即可直接調(diào)用f[0][n+1]得到答案

?

例題2

? 給出兩個串,求他們所有內(nèi)容不同的公共上升子序列個數(shù)
? ? = 5000, Σ = ??

不會

?

已知大串的子串問題

?

? 對于已知大串的題,一般是考慮維護一個結(jié)構(gòu),來表示這個大串
內(nèi)的所有子串,同時還能體現(xiàn)出這些子串的(前后綴)關(guān)系
? 一般的方法分為兩種:后綴樹(數(shù)組)、SAM
? 前者轉(zhuǎn)化為邊長和的統(tǒng)計,后者轉(zhuǎn)化為路徑的統(tǒng)計

(PS:先考慮SAM和后綴樹,畢竟是樹形結(jié)構(gòu)方便。SA其次考慮(HEOI2019 D1T2就這樣沒有想出來。。。))

?

?

例題 1

? 給出一棵樹,點上有字符
? 求所有的直上直下的路徑構(gòu)成的不同串有多少種
? ? ≤ 10^6,字符集較大

字符集大,不能用廣義SAM

剩下后綴數(shù)組

直接每個點到跟的路徑看做后綴

后綴排序。倍增,和樹上倍增恰好契合,直接利用2^(k-1)的排序結(jié)果即可。

求height用hash+二分,

ans=∑dep-∑hei

?

例題2

? 給出一個串
? 每次給出 ?, ?, ?,在 ?, ? 中找一個 ? 使得 LCP(?(k,n), ?(i,n) ) 最大
? ?, ? ≤ 10^6

k的后綴的排名在[l,r]的前驅(qū)后繼找到即可

后綴數(shù)組+主席樹(前綴后綴)

后綴數(shù)組+離線線段樹

?

例題3

加強:[HEOI2016/TJOI2016]字符串?

二分LCP長度mid,看rk[c]最多延伸的區(qū)間[le,ri],看這個區(qū)間里面是否有sa為[a,b-mid+1]的

主席樹差分判斷是否出現(xiàn)

注意:

1.lcp時候,++p1

2.rk,sa別寫反

#include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') #define pb push_back #define solid const auto & #define enter cout<<endl #define pii pair<int,int> using namespace std; typedef long long ll; template<class T>il void rd(T &x){char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x); } template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}namespace Miracle{ const int N=1e5+5; int n,m; char s[N]; int hei[N],sa[N],rk[N]; int xx[N],yy[N],buc[N]; int f[N][18],lg[N]; void SA(){int m=130;int *x=xx,*y=yy;for(reg i=1;i<=n;++i) ++buc[x[i]=s[i]];for(reg i=1;i<=m;++i) buc[i]+=buc[i-1];for(reg i=1;i<=n;++i) sa[buc[x[i]]--]=i;for(reg k=1;k<=n;k<<=1){int num=0;for(reg i=n-k+1;i<=n;++i) y[++num]=i;for(reg j=1;j<=n;++j){if(sa[j]-k>0) y[++num]=sa[j]-k;}for(reg i=1;i<=m;++i) buc[i]=0;for(reg i=1;i<=n;++i) ++buc[x[i]];for(reg i=2;i<=m;++i) buc[i]+=buc[i-1];for(reg i=n;i>=1;--i) sa[buc[x[y[i]]]--]=y[i],y[i]=0;swap(x,y);num=1;x[sa[1]]=1;for(reg i=2;i<=n;++i){x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&(y[sa[i]+k]==y[sa[i-1]+k]))?num:++num;}if(num==n) break;m=num;} } void HEI(){for(reg i=1;i<=n;++i) rk[sa[i]]=i;int k=0;for(reg i=1;i<=n;++i){if(k) --k;if(rk[i]==1) continue;int j=sa[rk[i]-1];while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;hei[rk[i]]=k;}for(reg i=1;i<=n;++i) {lg[i]=(i>>(lg[i-1]+1))?lg[i-1]+1:lg[i-1];f[i][0]=hei[i];}for(reg j=1;j<=17;++j){for(reg i=1;i+(1<<j)-1<=n;++i){f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);}}//;//build rmq } int lcp(int p1,int p2){// cout<<" lcp "<<p1<<" "<<p2<<" "<<n-rk[p1]+1<<endl;if(p1==p2) return n-sa[p1]+1;// p1=sa[p1],p2=sa[p2];if(p1>p2) swap(p1,p2);++p1;int len=lg[p2-p1+1];return min(f[p1][len],f[p2-(1<<len)+1][len]); } struct node{int ls,rs;int sum; }t[40*N]; int tot; int rt[N]; void pushup(int x){t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum; } void upda(int &x,int y,int l,int r,int p){x=++tot;t[x]=t[y];if(l==r){++t[x].sum;return;}int mid=(l+r)>>1;if(p<=mid) upda(t[x].ls,t[y].ls,l,mid,p); else upda(t[x].rs,t[y].rs,mid+1,r,p);pushup(x); } int query(int x,int y,int l,int r,int L,int R){if(L<=l&&r<=R){return t[x].sum-t[y].sum;}int mid=(l+r)>>1;int ret=0;if(L<=mid) ret+=query(t[x].ls,t[y].ls,l,mid,L,R);if(mid<R) ret+=query(t[x].rs,t[y].rs,mid+1,r,L,R);return ret; } bool che(int a,int b,int pos,int len){b=b-len+1;if(a>b) return false;if(!len) return true;pos=rk[pos];// cout<<" che "<<a<<" "<<b<<" pos "<<pos<<" md "<<len<<endl;int le=pos;int L=1,R=pos;while(L<=R){int mid=(L+R)>>1;int now=lcp(mid,pos);if(now>=len) le=mid,R=mid-1;else L=mid+1;}L=pos,R=n;int ri=pos;while(L<=R){int mid=(L+R)>>1;int now=lcp(pos,mid);// cout<<mid<<" now "<<now<<endl;if(now>=len) ri=mid,L=mid+1;else R=mid-1;}// cout<<" le "<<le<<" ri "<<ri<<endl;int lp=query(rt[ri],rt[le-1],1,n,a,b);if(lp>0) return true;return false; } int main(){rd(n);rd(m);scanf("%s",s+1);SA();HEI();// prt(sa,1,n);// prt(rk,1,n);// prt(hei,1,n);for(reg i=1;i<=n;++i){upda(rt[i],rt[i-1],1,n,sa[i]);}int a,b,c,d;while(m--){rd(a);rd(b);rd(c);rd(d);int L=0,R=d-c+1;int ans=0;while(L<=R){int mid=(L+R)>>1;if(che(a,b,c,mid)) ans=mid,L=mid+1;else R=mid-1;}printf("%d\n",ans);}return 0; }} signed main(){Miracle::main();return 0; }/*Author: *Miracle* */ View Code

?

?

字符串綜合練習

樹上后綴排序

luoguP5353 【模板】樹上后綴排序

倍增即可。常數(shù)優(yōu)化?

?

?看i207M的吧

巧妙運用三關(guān)鍵字排序,并且注意可重復、不可重復的搭配。

fa也不用倍增,直接循環(huán)找一遍即可。

還有我的辣雞代碼:(直接無視編號排好序,再sort+dfs)

#include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') #define pb push_back #define solid const auto & #define enter cout<<endl #define pii pair<int,int> using namespace std; typedef long long ll; template<class T>il void rd(T &x){char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x); } template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}namespace Miracle{ const int N=5e5+5; int n; vector<int>to[N]; char s[N]; int dep[N]; int fa[N][20]; int lim; void dfs1(int x){lim=max(lim,dep[x]);for(solid y:to[x]){dep[y]=dep[x]+1;dfs1(y);} } int mem[N],num; int sa[N],rk[N]; int xx[2*N],yy[2*N],buc[N],pos[N]; void SA(){int m=130;int *x=xx,*y=yy;for(reg i=1;i<=n;++i) ++buc[x[i]=s[i]];for(reg i=1;i<=m;++i) buc[i]+=buc[i-1];for(reg i=1;i<=n;++i) sa[buc[x[i]]--]=i;for(reg k=0;k<20;++k){if((1<<k)>lim) break;// cout<<" kk "<<k<<endl;int num=0;for(reg i=1;i<=n;++i){if(fa[i][k]==0) y[++num]=i;}for(reg i=1;i<=m;++i) buc[i]=0;for(reg i=1;i<=n;++i){if(fa[i][k]){++buc[x[fa[i][k]]];}}for(reg i=1;i<=m;++i) {buc[i]+=buc[i-1];pos[i]=num+buc[i];}for(reg i=1;i<=n;++i){if(fa[i][k]){y[pos[x[fa[i][k]]]--]=i;}}for(reg i=1;i<=m;++i) buc[i]=0;for(reg i=1;i<=n;++i) ++buc[x[i]];for(reg i=1;i<=m;++i) buc[i]+=buc[i-1];for(reg i=n;i>=1;--i){sa[buc[x[y[i]]]--]=y[i];y[i]=0;}swap(x,y);num=1;x[sa[1]]=1;for(reg i=2;i<=n;++i){x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[fa[sa[i]][k]]==y[fa[sa[i-1]][k]])?num:++num;}if(num==n) break;m=num;} } bool cmp(int a,int b){return xx[a]==xx[b]?a<b:xx[a]<xx[b]; } void dfs2(int x){mem[++num]=x;for(solid y:to[x]){dfs2(y);} } int main(){rd(n);dep[1]=1;for(reg i=2;i<=n;++i){rd(fa[i][0]);to[fa[i][0]].push_back(i);}for(reg j=1;j<=19;++j){for(reg i=1;i<=n;++i){fa[i][j]=fa[fa[i][j-1]][j-1];}}dfs1(1);scanf("%s",s+1);SA();// prt(xx,1,n);memset(buc,0,sizeof buc);num=0;for(reg i=1;i<=n;++i){sort(to[i].begin(),to[i].end(),cmp);}dfs2(1);// prt(mem,1,num);for(reg i=1;i<=n;++i) ++buc[xx[i]];for(reg i=1;i<=n;++i) buc[i]+=buc[i-1];for(reg i=n;i>=1;--i){xx[mem[i]]=buc[xx[mem[i]]]--;sa[xx[mem[i]]]=mem[i];}// prt(xx,1,n);prt(sa,1,n);return 0; }} signed main(){Miracle::main();return 0; }/*Author: *Miracle* */ View Code

?

區(qū)間回文子串

給出一個串
每次詢問一個區(qū)間中有多少子區(qū)間是回文的

?, ? ≤ 10^6

manacher求出回文半徑p[],

根據(jù)在mid的左右分別考慮,左右分別和l,r卡邊界

開主席樹維護i-p[i],i+p[i]即可。

?

判斷AC自動機相等

給出兩個串的集合 A、B
判斷“所有包含 A 中一個串作為子串的串的集合”和“所有包含 B中
一個串作為子串的串的集合”是否相等
長度 ≤ 2000

即,是否存在一個串使得存在A的子串不存在B的或者存在B的不存在A的

直接從(x,y)表示A匹配到了x,B匹配到了y,都沒有出現(xiàn)過A,B作為子串

大力BFS

枚舉出邊1~26,如果到達一個能出現(xiàn),一個不能出現(xiàn)的狀態(tài)那么就不等了

能同時到達能出現(xiàn)的狀態(tài),不管。

還是都不能出現(xiàn),加入queue

?

THUSC2018

? (THUSC2018 D1T2)
? 給出 ? 和 ? 個和諧串,求有多少長度為 2? 的形如 ?? 的串,不
包含 ? 個和諧串不相交地出現(xiàn)

? ? ≤ 100, ? ≤ 200, s ≤ 5, ? ≤ 5

形如AA?

并不是枚舉分界點!

外層枚舉第二個A開始匹配的位置M

f[i][j][k]第一個A匹配到了i,第二個匹配到了j,劃分了k個

能劃分就直接劃分,回到起點

開始f[0][M][0] 最后就是f[M][*][<k]

后半段M開始也是直接能劃分就劃分

不可能最后得到的字符串劃分有相交

因為這樣,到了綠色位置就直接回起點了,不可能再到M了。

?

?

概率題

給出串集合 ?
我們隨機生成串,每次以 pi 的概率生成第 ? 個字母
一旦生成的串包含 ? 里面所有串作為子串,就停止
求期望生成多長

?? ≤ 8, ? ∈ ? ≤ 6?

狀壓。成環(huán)。高斯消元。對S相同的進行高斯消元,2^8*(50^3)

?

多串本質(zhì)不同子串

倒著做,
把si沒有出邊的指到下一個自動機的初始節(jié)點
DP統(tǒng)計路徑

每個本質(zhì)不同的串會在出現(xiàn)字典序最小的位置被統(tǒng)計一次

?

?

最長反鏈

給出一些字符串
你要從中選擇一個集合,使得任意一個串不是另一個串的子串
求集合的最大大小
? ≤ 2000, ∑?<= 10^7

fail樹的祖先和自己不能在一起

最長反鏈=最小鏈覆蓋

求最小鏈覆蓋即可。

?

51nod?1600 Simple KMP

? 給出一個字符串
? 對字符串的每個前綴,輸出這個前綴的 KMP 的 fail 樹的深度之和
? ? ≤ 10^5

不斷跳kmp,感覺像是統(tǒng)計貢獻

可以發(fā)現(xiàn),就是以i為結(jié)尾的子串在[1,i-1]的出現(xiàn)次數(shù),樹剖維護right集合大小即可

?

?

THUWC2018

給出一個大串 ?
每次給出一個小串 ?,詢問把 ? 插在 ? 中的哪一個位置能使插完
之后的字符串的字典序最小
多個位置輸出編號最小的位置
?, ∑? ≤ 10^6

考慮為什么要插入t,一定要使得比s的字典序要大,否則不如插到最后面

所以考慮和原串的字典序關(guān)系。

枚舉lcp=0~|t|,看下一個!

也就是t在s上匹配

匹配到了t的前綴i

SAM上找存在下一個字符<t[i+1]的最靠前的起始位置j,把j插入一個數(shù)組里

一旦失配就break

全部匹配?。。。。還是考慮下一個比t[1]字典序小的最靠前的位置

s最前面嘗試插入t

s最后2*|t|位置嘗試插入t

這些嘗試都加入數(shù)組

哈希+二分判斷

?

這樣我們每個插入位置i以插入后與s[1..i]+t的lcp為標準進行考慮了。

?

轉(zhuǎn)載于:https://www.cnblogs.com/Miracevin/p/10805633.html

總結(jié)

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

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