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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

BZOJ.4180.字符串计数(后缀自动机 二分 矩阵快速幂/倍增Floyd)

發布時間:2024/4/17 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BZOJ.4180.字符串计数(后缀自动机 二分 矩阵快速幂/倍增Floyd) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接

先考慮 假設S確定,使構造S操作次數最小的方案應是:對T建SAM,S在SAM上匹配,如果有S的轉移就轉移,否則操作數++,回到根節點繼續匹配S。即每次操作一定是一次極大匹配。

簡單證明:假設S="ABCD",T有子串"A","AB","CD","BCD",那么步數最小方案是選"AB"再接上"CD",而不是提前斷開選擇"A"+"BCD",因為后者只會使后面的子串變長,"CD"有可能繼續接子串而"BCD"卻不能。

那么對于本題,我們要使操作次數多,拼接的子串盡量短,應是選擇最短的到達一個不能匹配某字符的位置,即!son[x][c]。那么下次便是從根沿\(son[root][c]\)再挑一個結束字符沿最短路徑走。
而且字符集大小只有4。令\(f[i][j]\)表示從根節點沿字符\(i\)出邊出發,到達某個沒有字符\(j\)轉移的節點的最短路徑。
那么兩次操作形成的S長度為\(l[i][k]=f[i][j]+f[j][k]\),于是考慮二分操作次數\(m\),求\(m\)次操作后可以得到的\(S\)最短的長度是否\(\leq n\)。可以用矩陣快速冪/倍增Floyd加速轉移。
\(f\)可以在SAM上求。令\(g[x][c]\)表示在\(x\)節點到達一個沒有\(c\)轉移的節點的最短距離,則\(g[x][c]=\min\{g[son[x]][c]+1\}\)
最后\(f[i][j]=g[son[1][i]][j]+1\)

不是枚舉子節點更新\(fa[x]\) 而是枚舉\(son[x]\)更新\(x\)啊mdzz。
操作次數會達到longlong。


另外可以直接轉化為圖上問題:https://blog.csdn.net/kscla/article/details/79504779
即設可轉移邊邊權為0,不能轉移的連回根節點對應轉移點,邊權為1。那么就是從根節點出發,走n步,求最大價值。還是縮下沒用的邊然后二分+倍增Floyd。


INF要設為2e18不是1e18,否則minlen==n時直接ans=mid,break不對。。(大概是數據問題就這里不對)
被INF卡還行。


//10296kb 384ms #include <cstdio> #include <cstring> #include <algorithm> typedef long long LL; const int N=2e5+7; const LL INF=2e18;//!...struct Suffix_Automaton {int tot,las,fa[N],son[N][4],len[N],A[N],tm[N],g[N][4],f[4][4];LL n;//,g[N][4],f[4][4];char s[N>>1];struct Matrix{LL a[4][4];Matrix operator *(const Matrix &x)const{Matrix res;for(int i=0; i<4; ++i)for(int j=0; j<4; ++j){LL tmp=INF;for(int k=0; k<4; ++k)tmp = std::min(tmp, a[i][k]+x.a[k][j]);res.a[i][j]=tmp;}return res;}}Base;Matrix FP(Matrix x,LL k){Matrix t=x;for(--k; k; k>>=1, x=x*x)if(k&1) t=t*x;return t;}void Insert(int c){int p=las,np=++tot; len[las=np]=len[p]+1;for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;if(!p) fa[np]=1;else{int q=son[p][c];if(len[q]==len[p]+1) fa[np]=q;else{int nq=++tot; len[nq]=len[p]+1;memcpy(son[nq],son[q],sizeof son[q]);fa[nq]=fa[q], fa[q]=fa[np]=nq;for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;}}}void Build(){las=tot=1;scanf("%lld%s",&n,s+1); int l=strlen(s+1);for(int i=1; i<=l; ++i) Insert(s[i]-'A');for(int i=1; i<=tot; ++i) ++tm[len[i]];for(int i=1; i<=l; ++i) tm[i]+=tm[i-1];for(int i=1; i<=tot; ++i) A[tm[len[i]]--]=i;memset(g,0x3f,sizeof g);for(int i=1; i<=tot; ++i)for(int j=0; j<4; ++j)if(!son[i][j]) g[i][j]=0;for(int i=tot,x=A[i]; i; x=A[--i])for(int j=0,s; j<4; ++j)if(s=son[x][j])for(int k=0; k<2; ++k)g[x][k]=std::min(g[x][k],g[s][k]+1),g[x][k+2]=std::min(g[x][k+2],g[s][k+2]+1);//閑的...但還沒都展開...for(int i=0; i<4; ++i)for(int j=0; j<4; ++j)Base.a[i][j]=g[son[1][i]][j]+1;}int Check(LL x){Matrix res = FP(Base,x);int s=0;for(int i=0; i<4; ++i)for(int j=0; j<4; ++j)if(res.a[i][j]==n) s=1;else if(res.a[i][j]<n) return 2;return s;//最短長度=n已經最優了 <n則x次一定不夠 }void Solve(){LL l=1,r=n,mid,ans=1,s;while(l<=r){if((s=Check(mid=l+r>>1))==1) {ans=mid; break;}//ans=mid, r=mid-1;else if(!s) r=mid-1;else ans=l=mid+1;}printf("%lld\n",ans);} }sam;int main() {sam.Build(), sam.Solve();return 0; }

轉載于:https://www.cnblogs.com/SovietPower/p/9249426.html

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的BZOJ.4180.字符串计数(后缀自动机 二分 矩阵快速幂/倍增Floyd)的全部內容,希望文章能夠幫你解決所遇到的問題。

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