最小表示法 乙個迴圈串中,字典序最小的串

2021-09-28 15:10:50 字數 1669 閱讀 7409

最小表示法使得乙個環形字串有唯一的讀法,這個讀法是所有讀法中字典序最小的。

什麼意思呢?

給你乙個字串 :1234567

他的讀法有

1234567,2345671,3456712,4567123等等

字典序最小的讀法很明顯就是1234567

本文講解的是最小表示法的o(n)求法。

對於這種環形問題,乙個常規的做法是把它自己複製一遍,接到原串之後。問題就轉換成了求在字串s中長度為n的最小子串。

如果我們使用暴力法來進行求解,就需要乙個串來保留最小串串行,然後將該序列與所有讀法進行比較,遇上比他小的,就進行替換,然後再進行比較,知道選出乙個最小的位置,這種情況下最壞的時間複雜度為n*s,s為字串的長度,但是其實,對於我們乙個隨機的字串來說,這樣的演算法其實已經非常優秀了,基本上不可能達到n*s這種複雜度。

最小表示法!

什麼是最小表示法,在我們上面講解暴力的過程中,我們依次的將原串中的各種讀法與最小串串行進行比較,但是很多比較其實是完全沒有必要的,我們可以直接進行排除。

假設我們現在有兩個開始位置i,j在原串上。這兩開始位置依次往後進行比較,兩個串的前k個字元都是相同的,第k+1個字元不同了。這時候我們就可以直接將i或者j往後移動k個位置,然後兩個串再進行比較(i和j串開頭的串中哪個k+1的字元大·,那麼就向後移動),為什麼,下面告訴你答案!

再仔細思考暴力的過程。我們假設前面s[i~k]與s[j~k]都是相同的(假設s[i+k+1]

在上圖中我們的i+k位置和j+k的位置不同了,且j位置的小,然後將可以將i移動到i+k位置,因為前k-1個字元相同,而d第k個不同,所以我們知道1一定是大於4的,2一定是大於5的,3一定是大於6的,所以123開始的位置一定不會是最小的讀法,所以就直接跳過了,這樣就加速了

還有乙個很重要的點,我在**中進行說明了

**實現:

#include#include#include#define max 100

int main()

// 因為兩個開始位置都向後移動

// 所以i向後移動的時候,如果i+k的值還是小於j的話

// 那麼i可以直接移動到j後面,因為j之前在往後跳的時候就能保證j

// 前面的位置開始的字串不可能是最小的,所以可以直接移動到j後面

if(i < j)

k = 0;

}else if(str[ie] < str[je])

if(j < i)

k = 0;

}else

}if(i > j)

}void get_next(char *str,int len,int *next)

next[0] = -1;

if(len == 1)

next[1] = 0;

if(len == 2)

int i=2,j=0;

for(;i<=len;)else if(j > 0)else

}}int express(char *str,int len,int flag)

}else

}k=0;

}return i > j ? j : i;

}

字典序法求乙個陣列的全排列4

全排列演算法,大致來講有四種字典序法,遞增進製數法,遞減進製數法,鄰位對換法,這裡講一下字典序方法。首先看什麼叫字典序,顧名思義就是按照字典的順序 a z,1 9 以字典序為基礎,我們可以得出任意兩個數字串的大小。比如字串 abcdg 和 abcfdg 我們從左到右比較每乙個字元的大小,發現第四個字...

有乙個n m的格仔,求起點到終點的最小字典序

zyh相信自己想要的幸福在不遠處。然而,zyh想要得到這幸福,還需要很長的一段路。zyh堅持認為整個人生可以抽象為乙個n m的棋盤。左上角的格仔為 1,1 右下角的格仔為 n,m 整個棋盤上的格仔都有不同的事件,因為生活的多姿多彩,事件的權值aij都兩兩不同。不幸的是,在整個人生中有若干個極其黑暗的...

給乙個有向樹,輸出字典序最小的那個拓撲排序

乙個完整的軟體專案往往會包含很多由 和文件組成的原始檔。編譯器在編譯整個專案 的時候,可能需要按照依賴關係來依次編譯每個原始檔。比如,a.cpp依賴b.cpp,那麼 在編譯的時候,編譯器需要先編譯b.cpp,才能再編譯a.cpp。假設現有0,1,2,3四 個檔案,0號檔案依賴1號檔案,1號檔案依賴2...