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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

几个冷门字符串算法的学习笔记(最小表示法,exKMP,Lyndon Word)

發布時間:2023/12/3 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 几个冷门字符串算法的学习笔记(最小表示法,exKMP,Lyndon Word) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

所有下標均從1開始

最小表示法

給定一個串,求字典序最小的循環同構。

我們把串復制一遍接在后面,然后求出[1,N][1,N][1,N]開始的長為NNN的子串中最小的

先設i=1,j=2i=1,j=2i=1,j=2

然后暴力找出iiijjj往后匹配的第一個不同的位置,記為i+ki+ki+kj+kj+kj+k

如果Si+k<Sj+kS_{i+k}<S_{j+k}Si+k?<Sj+k?,說明iiijjj優,所以jjj不是最優解;然后發現i+1i+1i+1j+1j+1j+1優,所以j+1j+1j+1不是最優解……這樣可以讓jjj直接跳到j+k+1j+k+1j+k+1

Si+k>Sj+kS_{i+k}>S_{j+k}Si+k?>Sj+k?同理

如果i=ji=ji=j,隨便讓一個+1+1+1即可

兩個指針都不能超過NNN,一個超過之后另一個就是答案

因為所有位置都會被遍歷,而最優解一定不會被丟掉,所以正確性可以保證。

復雜度顯然是O(N)O(N)O(N)

模板題

#include <iostream> #include <cstdio> #include <cstring> using namespace std; char s[10005]; int main() {int T;scanf("%d",&T);while (T--){scanf("%s",s);int n=strlen(s);int i=0,j=1;while (i<n&&j<n)for (int k=0;;k++){if (s[(i+k)%n]!=s[(j+k)%n]){if (s[(i+k)%n]>s[(j+k)%n])i+=k+1;else j+=k+1;if (i==j) j++;break;}if (k==n) goto end;}end:printf("%d\n",min(i,j)+1);}return 0; }

(遠古代碼,和上面講的略有不同,僅供參考)

擴展KMP

官方名稱應該叫Z算法,不知道為啥傳到國內就變成擴展KMP了

但實際上思想和manacher很像所以應該叫擴展馬拉車

解決的問題是給兩個串S,TS,TS,T,求 TTT的每個后綴和SSS 的最長公共前綴

先把SSS接在TTT后面,中間加個#之類的東西 把這個串記為AAA

然后設pip_ipi?表示AAA的從iii開始的后綴和TTT(也可以是AAA)的最長公共前綴

并且設公共前綴擴展到的最右位置為mxmxmx,取到這個最大值的iiixxx

然后iii222開始遍歷(因為p1p_1p1?沒有意義還會把算法搞砸)

如果i<mxi<mxi<mx


因為上下橙色位置相同,所以pi=pi?x+1p_i=p_{i-x+1}pi?=pi?x+1?,當然要和mx?i+1mx-i+1mx?i+1min?\minmin

如果i≥mxi \geq mximx,不管

然后暴力擴展,更新mxmxmx,沒了

復雜度顯然O(∣S∣+∣T∣)O(|S|+|T|)O(S+T)

模板題

#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #define MAXN 200005 using namespace std; char s[MAXN],t[MAXN]; int p[MAXN]; int main() {scanf("%s%s",t+1,s+1);int m=strlen(s+1);strcat(s+1,"#");strcat(s+1,t+1);int n=strlen(s+1);for (int i=2,x=0,mx=0;i<=n;i++){p[i]=i<=mx? min(p[i-x+1],mx-i+1):0;while (s[i+p[i]]==s[p[i]+1]) ++p[i];if (i+p[i]-1>mx) x=i,mx=i+p[i]-1;}for (int i=1;i<=n;i++)if (s[i]=='#') puts("");else printf("%d ",i>1? p[i]:m);return 0; }

Lyndon Word

定義:一個串是Lyndon Word(以下簡稱LW),當且僅當它本身是自己字典序最小的后綴

下文字符串的比較均為字典序,+為字符串拼接

性質1 兩個LW u,vu,vu,v,如果u<vu<vu<v,那么u+vu+vu+v是LW

對于vvv的后綴,它比vvv大,所以一定不是最小的;

對于vvv,因為u<vu<vu<v,所以u+v<vu+v<vu+v<v

對于(u的后綴)+v(u的后綴)+v(u)+v,因為u<(u的后綴)u<(u的后綴)u<(u),所以u+v<(u的后綴)+vu+v<(u的后綴)+vu+v<(u)+v

所以u+vu+vu+v是最小的

所以LW可以遞歸定義:

  • 單個字符是LW
  • 多個字典序遞增的LW順次拼接后是LW
  • 性質2 一個LW將最后一個字符變大后仍是LW

    只有最后一個只包含一個字符的后綴變大,前面大小關系不變

    性質3 任意字符串SSS存在且僅存在一種分解方式S=s1+s2+...+snS=s_1+s_2+...+s_nS=s1?+s2?+...+sn?,使得所有sis_isi?均為LW且單調不增

    證明是不可能的,這輩子都是不可能的

    把性質3中的分解稱為Lyndon分解

    接下來要講的就是線性求Lyndon分解的Duval算法

    首先三個指針i,j,ki,j,ki,j,k,表示iii以前的分解已經固定,現在處理第kkk個字符,jjj一會兒說

    [1,i)[1,i)[1,i)s1+s2+...+sns_1+s_2+...+s_ns1?+s2?+...+sn?,其中sis_isi?為LW且單調不增

    [i,k)[i,k)[i,k)t+t+...+t+t1t+t+...+t+t_1t+t+...+t+t1?,其中ttt是LW,t1t_1t1?ttt的可空前綴

    也就是一個LW不斷循環,最后一個循環節可以不完整。注意這不一定是[i,k)[i,k)[i,k)的Lyndon分解,因為t1t_1t1?不一定是LW

    別問為啥,問就是歸納法

    現在把SkS_kSk?加在后面,如果要繼續循環,應該加的是Sk?循環節長度S_{k-循環節長度}Sk??,我們把這個kkk應該跟的位置記為jjj

    如果Sj=SkS_j=S_kSj?=Sk?,說明循環正常,繼續往后

    如果Sj<SkS_j<S_kSj?<Sk?,根據性質1,最后一個不完整的循環節t1t_1t1?加上SkS_kSk?是個LW并且比前面的ttt都大,不斷向前合并發現整段都是LW。所以將[i,k][i,k][i,k]一長串合并成新的ttt,即令j=ij=ij=i

    如果Sj>SkS_j>S_kSj?>Sk? 不管t1t_1t1?SkS_kSk?大小關系,反正后面怎么加怎么都會小于ttt,所以沒ttt啥事了,把所有ttt固定下來,t1t_1t1?作為新的循環節。然后t1t_1t1?這個地方,我們之前以為它會進入循環,然而它沒有,這里面漏了一些信息,所以需要從t1t_1t1?的開頭重新分解

    模板題

    #include <iostream> #include <cstdio> #include <cstring> #include <cctype> #define MAXN (1<<20)+5 using namespace std; char s[MAXN]; int main() {scanf("%s",s+1);int n=strlen(s+1);for (int i=1;i<=n;){int j=i,k=i+1;while (s[j]<=s[k]){if (s[j]==s[k]) ++j;else j=i;++k;}while (i<=j){printf("%d ",i+k-j-1);i+=k-j;}}return 0; }

    我 華 燈 宴 呢

    總結

    以上是生活随笔為你收集整理的几个冷门字符串算法的学习笔记(最小表示法,exKMP,Lyndon Word)的全部內容,希望文章能夠幫你解決所遇到的問題。

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