今天又在lyk大佬的博客學會了——最小表示法(異常激動
發篇題解記念一下
說在前面:給luogu提個建議最小表示法的題太少了,都被hdu搶去了!!!php
咱們先看一下題目html
看完後能夠用一個字歸納——蒙,兩個字——懵逼c++
在這裏我提供題目大意:算法
輸出最大和最小的是從哪一位開始的,同時輸出最小循環節的個數。數組
因爲本人懶於寫字符串最小表示法,那麼咱們就來借鑑一下lykkk的優秀總結函數
看完以後,顯然咱們就明白了許多spa
由於題目中讓咱們同時求出最大和最小的起始位置code
因此咱們不只要來一遍最小表示法,還要來一遍最大表示法htm
其實這兩種算法惟一的區別就是:blog
最小表示法中當str[i+k]>str[j+k]時,i+k的字典序比j+k的字典序大,那麼咱們就要拋棄當前以i爲頭的字符串,日後走即i+=k+1
最大表示法就是當str[i+k]<str[j+k]時,咱們纔要更新起點
各來一遍後,題目就完成了一半
至於KMP在這裏的做用就是,利用next數組來求循環節,則次數=長度/循環節長度
說到這裏,顯然三個子函數足以解決這個問題了
咱們最後只須要在主函數里根據題意輸入輸出便可
無代碼,不成方圓
#include<bits/stdc++.h> using namespace std; const int N = 1100000; int n; char s[N]; int nxt[N]; void Kmp(int l){//用來求最小循環節的個數 int j=0,k=nxt[0]=-1; while(j<l){ if(k==-1 || s[j]==s[k]) nxt[++j]=++k; else k=nxt[k]; } } int Min(char s[],int l){ int i=0,j=1,k=0; while(i<l && j<l && k<l){ if(s[(i+k)%l]==s[(j+k)%l]) k++; else if(s[(i+k)%l]>s[(j+k)%l]) i+=k+1,k=0; else j+=k+1,k=0; if(i==j) i++; } return min(i,j); } int Max(char s[],int n){ int i=0,j=1,k=0; while(i<n && j<n && k<n){ if(s[(i+k)%n]==s[(j+k)%n]) k++; else if(s[(i+k)%n]<s[(j+k)%n]) i+=k+1,k=0;//惟一的區別 else j+=k+1,k=0; if(i==j) i++; } return min(i,j); } int main(){//常規操做 while(scanf("%s",s)!=EOF){ int len=strlen(s); Kmp(len); int maxn=Max(s,len),minn=Min(s,len); printf("%d %d %d %d\n",minn+1,len/(len-nxt[len]),maxn+1,len/(len-nxt[len])); } return 0; }
完結,撒花!