题意:给你一个字符串,让你输出它移位后最小字典序的rank和循环节个数,最大字典序的rank和循环节个数。
题解:循环节好找,只要判断len%(len-nx[len])是否等于0,如果等于0说明含有循环节,则ans=len%(len-nx[len]),否则ans=1(即循环节为一整个字符串)。最小字典序和最大字典序的rank其实就是其首在原字符串的下标+1。做的时候去学习了一下最小最大表示法 感谢大佬的模版+讲解:https://www.cnblogs.com/cenariusxz/p/4903387.html
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<cstring> using namespace std; const int N=1e6+5; using namespace std; int nx[N]; char str[N<<1]; int len; void getnx(int x){ memset(nx,0,sizeof(nx)); int k=-1,i=0; nx[0]=-1; while(i<x){ if(k==-1||str[k]==str[i]){ nx[++i]=++k; } else k=nx[k]; } } int find_min_max(int flag){//0表示找最小 1表示找最大 int i=0,j=1;//用i和j两个下标来表示两个字符串 while(i<len&&j<len){ int k=0; while(str[i+k]==str[j+k])k++;//不断比较直到比较完长度为l的串或两个子串不相等 if(k>=len)return min(i,j);//当比较长度大于等于len的时候,返回最小的首下标 if(flag==0){ if(str[i+k]>str[j+k])i=max(i+k+1,j+1);//i串比j串大,那么i到i+k中的串都比j串大,i可以直接移动到i+k+1位置,而起始位置比j小的肯定都在j移动过程中比较过,所以i可以直接移动到j+1位置,因此取这两值的最大值(这句话是复制过来的~~~) else j=max(j+k+1,i+1); } else{ if(str[i+k]<str[j+k])i=max(i+k+1,j+1); else j=max(j+k+1,i+1); } } return min(i,j); } int main(){ while(~scanf("%s",str)){ len=strlen(str); getnx(len); for(int i=0;i<len;i++)str[i+len]=str[i];//将字符串复制一倍 int ans; if(len%(len-nx[len])==0)ans=len/(len-nx[len]); else ans=1; int a=find_min_max(0)+1; int b=find_min_max(1)+1; printf("%d %d %d %d ",a,ans,b,ans); } return 0; }