今天又在lyk大佬的博客學會了——最小表示法(異常激動
發篇題解紀念一下
說在前面:給luogu提個建議最小表示法的題太少了,都被hdu搶去了!!!
我們先看一下題目
看完後可以用一個字概括——蒙,兩個字——懵逼
在這裏我提供題目大意:
輸出最大和最小的是從哪一位開始的,同時輸出最小循環節的個數。
由於本人懶於寫字符串最小表示法,那麼我們就來借鑑一下lykkk的優秀總結
看完之後,顯然我們就明白了許多
因爲題目中讓我們同時求出最大和最小的起始位置
所以我們不僅要來一遍最小表示法,還要來一遍最大表示法
其實這兩種算法唯一的區別就是:
最小表示法中當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; }
完結,撒花!