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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

SPOJ 1812 LCS2 - Longest Common Substring II (后缀自动机、状压DP)

發(fā)布時間:2025/3/15 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SPOJ 1812 LCS2 - Longest Common Substring II (后缀自动机、状压DP) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

手動博客搬家: 本文發(fā)表于20181217 23:54:35, 原地址https://blog.csdn.net/suncongbo/article/details/85058680

人生第一道后綴自動機。
說實話SAM我還沒學(xué)多么明白。
但是題還是要做的。
說起來這玩意真的很妙。可惜我智商低理解不了。
再次驗證了代碼能力菜到?jīng)]邊。hyw 30min寫完我寫2.5h.

題目鏈接 (洛谷)https://www.luogu.org/problemnew/show/SP1812

題目大意
\(n\)個長度為\(l_i\)的小寫字母字符串,求它們的最長公共子串 (要求每個字符串都要出現(xiàn)。) \(n\le 10, l_i\le 10^5\)

題解
做法一 二分+hash判斷。\(O(L\log L\times n)\), 據(jù)說會TLE.
做法二 后綴數(shù)組。不會。
做法三 后綴自動機。用后綴自動機的有兩種做法做法一
首先考慮如何求兩個串\(A,B\)\(LCS\). 對\(A\)串建出后綴自動機,用\(B\)串在上面匹配。
匹配時從頭到尾枚舉\(B\)的每一個字符,記錄當(dāng)前\(A\)串后綴自動機的位置\(pos\)以及當(dāng)前長度\(len\), 初始\(pos=rtn, len=0\) (\(rtn\)為根節(jié)點)

  • 當(dāng)前存在一條匹配邊。即son[pos][str[i]]!=0, 則\(pos\)跳到\(son[pos][str[i]]\), \(len\)增加1即可。
  • 當(dāng)前不存在一條匹配邊。即son[pos][str[i]]==0, 則\(pos\)\(u\)開始向上跳,直到pos==0或者son[pos][str[i]]==0.分別對應(yīng)第3和1種情況。此時\(len\)應(yīng)置為\(Len[pos]\), \(Len[u]\)表示\(u\)狀態(tài)表示的最長字符串。
  • 如果u==0表明我們匹配到了自動機外面,則此時應(yīng)重置\(pos=rtn,len=0\)
    這樣我們可以處理\(n=2\)的情形。\(n>2\)?
    對于\(A_2, A_3,...,A_n\)分別跑一次,每一個節(jié)點記錄一下匹配大小的最小值,然后求最大即可。每個點記錄的最小值是因為要求這個子串同時是這\(n\)個串的公共部分,求最大是對于合法的狀態(tài)求出最大值。
    做完了。?
    少了一步
    觀察到\(n=2\)\(A\)若長度較長的子串可以匹配,那么長度較短的子串也可以匹配。因此我們需要每做完一個串進(jìn)行一遍更新:
  • if(fa[u]) mx[fa[u]] = max(mx[fa[u]],mx[u]);

    做完了嗎?
    我們發(fā)現(xiàn)實際上對每個點還有限制,就是\(mx[u]\le len[u]\).

    if(fa[u]) {mx[fa[u]] = max(mx[fa[u]],min(mx[u],len[fa[u]]));}

    真·做完了。

    代碼

    //Wrong Coding: //pos = fa[pos]; curl = len[pos]; Wrong Order //insertstr() len[np]=len[p]+1 Forgot #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define llong long long using namespace std;const int N = 2e5; const int M = 3e5; const int S = 26; int son[N+3][S+3]; int fa[N+3]; int len[N+3]; char a[N+3]; char b[N+3]; int buc[N+3]; int oid[N+3]; int ans[N+3]; int mx[N+3]; int sz[N+3]; int lpos,siz,rtn,lena;void insertstr(char ch) {int p = lpos,np; siz++; np = lpos = siz; sz[np] = 1; len[np] = len[p]+1;for(; p && son[p][ch]==0; p=fa[p]) son[p][ch] = np;if(p==0) {fa[np] = rtn;}else{int q = son[p][ch];if(len[p]+1==len[q]) {fa[np] = q;}else{siz++; int nq = siz; len[nq] = len[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));fa[nq] = fa[q]; fa[np] = fa[q] = nq;for(; p!=0 && son[p][ch]==q; p=fa[p]) son[p][ch] = nq;}} }void prework() {for(int i=1; i<=siz; i++) buc[len[i]]++;for(int i=1; i<=lena; i++) buc[i] += buc[i-1];for(int i=siz; i>=1; i--) oid[buc[len[i]]--] = i;for(int i=siz; i>=1; i--){int pos = oid[i];sz[fa[pos]] += sz[pos];} }void dfs(char str[],int lens) {int curl = 0,pos = rtn;for(int i=1; i<=lens; i++){while(pos && son[pos][str[i]-96]==0) {pos = fa[pos]; curl = len[pos];}if(pos) {curl++; pos = son[pos][str[i]-96]; mx[pos] = max(mx[pos],curl);}else {pos = rtn; curl = 0;}}for(int i=siz; i>=1; i--){int u = oid[i];if(fa[u]) {mx[fa[u]] = max(mx[fa[u]],min(mx[u],len[fa[u]]));}ans[u] = min(ans[u],mx[u]);mx[u] = 0;} }int main() {memset(ans,1,sizeof(ans));siz = 1; rtn = 1; lpos = 1;scanf("%s",a+1);lena = strlen(a+1);for(int i=1; i<=lena; i++) insertstr(a[i]-96);prework();while(scanf("%s",b+1)!=EOF){int lenb = strlen(b+1);dfs(b,lenb);}int fans = 0;for(int i=1; i<=siz; i++) {if(ans[i]<=5e6) fans = max(fans,ans[i]);}printf("%d\n",fans);return 0; }

    完了?沒有呢,還有做法二。做法二
    我們考慮這個題的一個加強版:每次給\(n\)個串的一個子集,詢問這個子集內(nèi)的串的\(LCS\). \(n\le 20, L\le 10^5, q\le 10^5\)
    做法:\(n\)個串并起來建立廣義\(SAM\). 然后每個狀態(tài)記錄一個\(n\)位二進(jìn)制數(shù)。對于第\(i\)個串先在其所到達(dá)狀態(tài)標(biāo)記\(2^i\). 最后把標(biāo)記沿著Parent樹從下往上更新一下,然后統(tǒng)計出每一個子集的答案,然后再用每個集合的答案更新它的子集的答案(注意避免\(O(3^n)\)\(TLE\))即可。
    時間復(fù)雜度\(O(2^nn+nL)\)
    可以參考GDOI2017微信這道題。見DC巨佬的博客:https://www.cnblogs.com/dcdcbigbig/p/10135665.html (引用已經(jīng)過博主同意orz)

    總結(jié)

    以上是生活随笔為你收集整理的SPOJ 1812 LCS2 - Longest Common Substring II (后缀自动机、状压DP)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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