hdu P3374 String Problem
今天又在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; }
完结,撒花!
你是我路过人间时藏在山河里的唯一秘密
也是我披星戴月奔赴万里无奈的触不可及